mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-21 14:51:34 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9f9d511e0 | ||
|
|
b8cb94c498 | ||
|
|
10decb7909 | ||
|
|
e0aab8d69d | ||
|
|
618600c753 | ||
|
|
d1aba87e37 | ||
|
|
db889f635e | ||
|
|
dd80e634f5 | ||
|
|
bec6fc1a74 | ||
|
|
5c96c7f99b | ||
|
|
7b9724f713 |
322
deploy_couchdb_to_flyio_v2_with_swap.ipynb
Normal file
322
deploy_couchdb_to_flyio_v2_with_swap.ipynb
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"id": "view-in-github",
|
||||||
|
"colab_type": "text"
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"<a href=\"https://colab.research.google.com/gist/vrtmrz/37c3efd7842e49947aaaa7f665e5020a/deploy_couchdb_to_flyio_v2_with_swap.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"History:\n",
|
||||||
|
"- 18, May, 2023: Initial.\n",
|
||||||
|
"- 19, Jun., 2023: Patched for enabling swap.\n",
|
||||||
|
"- 22, Aug., 2023: Generating Setup-URI implemented."
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "HiRV7G8Gk1Rs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "2Vh0mEQEZuAK"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Configurations\n",
|
||||||
|
"import os\n",
|
||||||
|
"os.environ['region']=\"nrt\"\n",
|
||||||
|
"os.environ['couchUser']=\"alkcsa93\"\n",
|
||||||
|
"os.environ['couchPwd']=\"c349usdfnv48fsasd\""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "SPmbB0jZauQ1"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Delete once\n",
|
||||||
|
"!rm ./fly.toml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "Nze7QoxLZ7Yx"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Installation\n",
|
||||||
|
"# You have to set up your account in here.\n",
|
||||||
|
"!curl -L https://fly.io/install.sh | sh\n",
|
||||||
|
"!/root/.fly/bin/flyctl auth signup"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "MVJwsIYrbgtx"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Generate server\n",
|
||||||
|
"!/root/.fly/bin/flyctl launch --auto-confirm --generate-name --detach --no-deploy --region ${region}\n",
|
||||||
|
"!/root/.fly/bin/fly volumes create --region ${region} couchdata --size 2 --yes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Check the toml once.\n",
|
||||||
|
"!cat fly.toml"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "2RSoO9o-i2TT"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "zUtPZLVnbvdQ"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Modify fly.toml\n",
|
||||||
|
"## Port modification\n",
|
||||||
|
"!sed -i 's/8080/5984/g' fly.toml\n",
|
||||||
|
"## Add user into.\n",
|
||||||
|
"!echo -e \"\\n[env]\\n COUCHDB_USER = \\\"${couchUser}\\\"\" >> ./fly.toml\n",
|
||||||
|
"## Set the location of an ini file which to save configurations persistently via erlang flags.\n",
|
||||||
|
"!echo -e \"\\nERL_FLAGS=\\\"-couch_ini /opt/couchdb/etc/default.ini /opt/couchdb/etc/default.d/ /opt/couchdb/etc/local.d /opt/couchdb/etc/local.ini /opt/couchdb/data/persistence.ini\\\"\" >> ./fly.toml\n",
|
||||||
|
"## Mounting volumes to store data and ini file.\n",
|
||||||
|
"!echo -e \"\\n[mounts]\\n source=\\\"couchdata\\\"\\n destination=\\\"/opt/couchdb/data\\\"\" >> ./fly.toml\n",
|
||||||
|
"!cat fly.toml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Make the Dockerfile to modify the permission of the ini file. If you want to use a specific version, you should change `latest` here.\n",
|
||||||
|
"!echo -e \"\\n[build]\\n dockerfile = \\\"./Dockerfile\\\"\" >> ./fly.toml"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "LQPsZ_dYxkTu"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"!echo -e \"FROM couchdb:latest\\nRUN sed -i '2itouch /opt/couchdb/data/persistence.ini && chmod +w /opt/couchdb/data/persistence.ini && fallocate -l 512M /swapfile && chmod 0600 /swapfile && mkswap /swapfile && echo 10 > /proc/sys/vm/swappiness && swapon /swapfile && echo 1 > /proc/sys/vm/overcommit_memory' /docker-entrypoint.sh\" > ./Dockerfile"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "44cBeGJ9on5i"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Check dockerfile\n",
|
||||||
|
"!cat ./Dockerfile"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "ai2R3BbpxRSe"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "xWdsTCI6bzk2"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Configure password\n",
|
||||||
|
"!/root/.fly/bin/flyctl secrets set COUCHDB_PASSWORD=${couchPwd}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "k0WIQlShcXGa"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Deploy server\n",
|
||||||
|
"# Be sure to shutdown after the test.\n",
|
||||||
|
"!/root/.fly/bin/flyctl deploy --detach --remote-only\n",
|
||||||
|
"!/root/.fly/bin/flyctl status"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"id": "0ySggkdlfq7M"
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import subprocess, json\n",
|
||||||
|
"result = subprocess.run([\"/root/.fly/bin/flyctl\",\"status\",\"-j\"], capture_output=True, text=True)\n",
|
||||||
|
"if result.returncode==0:\n",
|
||||||
|
" hostname = json.loads(result.stdout)[\"Hostname\"]\n",
|
||||||
|
" os.environ['couchHost']=\"https://%s\" % (hostname)\n",
|
||||||
|
" print(\"Your couchDB server is https://%s/\" % (hostname))\n",
|
||||||
|
"else:\n",
|
||||||
|
" print(\"Something occured.\")\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Finish setting up the CouchDB\n",
|
||||||
|
"# Please repeat until the request is completed without error messages\n",
|
||||||
|
"# i.e., You have to redo this block while \"curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to xxxx\" is showing.\n",
|
||||||
|
"!curl -X POST \"${couchHost}/_cluster_setup\" -H \"Content-Type: application/json\" -d \"{\\\"action\\\":\\\"enable_single_node\\\",\\\"username\\\":\\\"${couchUser}\\\",\\\"password\\\":\\\"${couchPwd}\\\",\\\"bind_address\\\":\\\"0.0.0.0\\\",\\\"port\\\":5984,\\\"singlenode\\\":true}\" --user \"${couchUser}:${couchPwd}\""
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "cGlSzVqlQG_z"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Please repeat until all lines are completed without error messages\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/chttpd/require_valid_user\" -H \"Content-Type: application/json\" -d '\"true\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/chttpd_auth/require_valid_user\" -H \"Content-Type: application/json\" -d '\"true\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/httpd/WWW-Authenticate\" -H \"Content-Type: application/json\" -d '\"Basic realm=\\\"couchdb\\\"\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/httpd/enable_cors\" -H \"Content-Type: application/json\" -d '\"true\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/chttpd/enable_cors\" -H \"Content-Type: application/json\" -d '\"true\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/chttpd/max_http_request_size\" -H \"Content-Type: application/json\" -d '\"4294967296\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/couchdb/max_document_size\" -H \"Content-Type: application/json\" -d '\"50000000\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/cors/credentials\" -H \"Content-Type: application/json\" -d '\"true\"' --user \"${couchUser}:${couchPwd}\"\n",
|
||||||
|
"!curl -X PUT \"${couchHost}/_node/nonode@nohost/_config/cors/origins\" -H \"Content-Type: application/json\" -d '\"app://obsidian.md,capacitor://localhost,http://localhost\"' --user \"${couchUser}:${couchPwd}\""
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "JePzrsHypY18"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"Now, our CouchDB has been surely installed and configured. Cheers!\n",
|
||||||
|
"\n",
|
||||||
|
"In the steps that follow, create a setup-URI.\n",
|
||||||
|
"\n",
|
||||||
|
"This URI could be imported directly into Self-hosted LiveSync, to configure the use of the CouchDB which we configured now."
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "YfSOomsoXbGS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Database config\n",
|
||||||
|
"import random, string\n",
|
||||||
|
"\n",
|
||||||
|
"def randomname(n):\n",
|
||||||
|
" return ''.join(random.choices(string.ascii_letters + string.digits, k=n))\n",
|
||||||
|
"\n",
|
||||||
|
"# The database name\n",
|
||||||
|
"os.environ['database']=\"obsidiannote\"\n",
|
||||||
|
"# The passphrase to E2EE\n",
|
||||||
|
"os.environ['passphrase']=randomname(20)\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"Your database:\"+os.environ['database'])\n",
|
||||||
|
"print(\"Your passphrase:\"+os.environ['passphrase'])"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "416YncOqXdNn"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Install deno for make setup uri\n",
|
||||||
|
"!curl -fsSL https://deno.land/x/install/install.sh | sh"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "C4d7C0HAXgsr"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Fetch module for encrypting a Setup URI\n",
|
||||||
|
"!curl -o encrypt.ts https://gist.githubusercontent.com/vrtmrz/f9d1d95ee2ca3afa1a924a2c6759b854/raw/d7a070d864a6f61403d8dc74208238d5741aeb5a/encrypt.ts"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "hQL_Dx-PXise"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"# Make buttons!\n",
|
||||||
|
"from IPython.display import HTML\n",
|
||||||
|
"result = subprocess.run([\"/root/.deno/bin/deno\",\"run\",\"-A\",\"encrypt.ts\"], capture_output=True, text=True)\n",
|
||||||
|
"text=\"\"\n",
|
||||||
|
"if result.returncode==0:\n",
|
||||||
|
" text = result.stdout.strip()\n",
|
||||||
|
" result = HTML(f\"<button onclick=navigator.clipboard.writeText('{text}')>Copy setup uri</button><br>Importing passphrase is `welcome`. <br>If you want to synchronise in live mode, please apply a preset after setup.)\")\n",
|
||||||
|
"else:\n",
|
||||||
|
" result = \"Failed to encrypt the setup URI\"\n",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "o0gX_thFXlIZ"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"colab": {
|
||||||
|
"provenance": [],
|
||||||
|
"private_outputs": true,
|
||||||
|
"include_colab_link": true
|
||||||
|
},
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"name": "python"
|
||||||
|
},
|
||||||
|
"gpuClass": "standard"
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 0
|
||||||
|
}
|
||||||
@@ -280,7 +280,7 @@ Now the CouchDB is ready to use from Self-hosted LiveSync. We can use `https://b
|
|||||||
|
|
||||||
## Automatic setup using Colaboratory
|
## Automatic setup using Colaboratory
|
||||||
|
|
||||||
We can perform all these steps by using [this Colaboratory notebook](https://gist.github.com/vrtmrz/b437a539af25ef191bd452aae369242f) without installing anything.
|
We can perform all these steps by using [this Colaboratory notebook](/deploy_couchdb_to_flyio_v2_with_swap.ipynb) without installing anything.
|
||||||
|
|
||||||
## After testing / before creating a new instance
|
## After testing / before creating a new instance
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.19.16",
|
"version": "0.19.19",
|
||||||
"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.19.16",
|
"version": "0.19.19",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.16",
|
"version": "0.19.19",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.16",
|
"version": "0.19.19",
|
||||||
"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",
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
if (!filename) continue;
|
if (!filename) continue;
|
||||||
if (ignorePatterns.some(e => filename.match(e)))
|
if (ignorePatterns.some(e => filename.match(e)))
|
||||||
continue;
|
continue;
|
||||||
if (!await this.plugin.isIgnoredByIgnoreFiles(filename)) {
|
if (await this.plugin.isIgnoredByIgnoreFiles(filename)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,7 +434,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async storeInternalFileToDatabase(file: InternalFileInfo, forceWrite = false) {
|
async storeInternalFileToDatabase(file: InternalFileInfo, forceWrite = false) {
|
||||||
if (!await this.plugin.isIgnoredByIgnoreFiles(file.path)) {
|
if (await this.plugin.isIgnoredByIgnoreFiles(file.path)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const id = await this.path2id(file.path, ICHeader);
|
const id = await this.path2id(file.path, ICHeader);
|
||||||
@@ -498,7 +498,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
const id = await this.path2id(filename, ICHeader);
|
const id = await this.path2id(filename, ICHeader);
|
||||||
const prefixedFileName = addPrefix(filename, ICHeader);
|
const prefixedFileName = addPrefix(filename, ICHeader);
|
||||||
const mtime = new Date().getTime();
|
const mtime = new Date().getTime();
|
||||||
if (!await this.plugin.isIgnoredByIgnoreFiles(filename)) {
|
if (await this.plugin.isIgnoredByIgnoreFiles(filename)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await runWithLock("file-" + prefixedFileName, false, async () => {
|
await runWithLock("file-" + prefixedFileName, false, async () => {
|
||||||
@@ -544,7 +544,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
async extractInternalFileFromDatabase(filename: FilePath, force = false) {
|
async extractInternalFileFromDatabase(filename: FilePath, force = false) {
|
||||||
const isExists = await this.app.vault.adapter.exists(filename);
|
const isExists = await this.app.vault.adapter.exists(filename);
|
||||||
const prefixedFileName = addPrefix(filename, ICHeader);
|
const prefixedFileName = addPrefix(filename, ICHeader);
|
||||||
if (!await this.plugin.isIgnoredByIgnoreFiles(filename)) {
|
if (await this.plugin.isIgnoredByIgnoreFiles(filename)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return await runWithLock("file-" + prefixedFileName, false, async () => {
|
return await runWithLock("file-" + prefixedFileName, false, async () => {
|
||||||
@@ -568,7 +568,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
await this.app.vault.adapter.remove(filename);
|
await this.app.vault.adapter.remove(filename);
|
||||||
try {
|
try {
|
||||||
//@ts-ignore internalAPI
|
//@ts-ignore internalAPI
|
||||||
await app.vault.adapter.reconcileInternalFile(filename);
|
await this.app.vault.adapter.reconcileInternalFile(filename);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
||||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||||
@@ -581,7 +581,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
await this.app.vault.adapter.writeBinary(filename, base64ToArrayBuffer(fileOnDB.data), { mtime: fileOnDB.mtime, ctime: fileOnDB.ctime });
|
await this.app.vault.adapter.writeBinary(filename, base64ToArrayBuffer(fileOnDB.data), { mtime: fileOnDB.mtime, ctime: fileOnDB.ctime });
|
||||||
try {
|
try {
|
||||||
//@ts-ignore internalAPI
|
//@ts-ignore internalAPI
|
||||||
await app.vault.adapter.reconcileInternalFile(filename);
|
await this.app.vault.adapter.reconcileInternalFile(filename);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
||||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||||
@@ -591,14 +591,14 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
} else {
|
} else {
|
||||||
const contentBin = await this.app.vault.adapter.readBinary(filename);
|
const contentBin = await this.app.vault.adapter.readBinary(filename);
|
||||||
const content = await arrayBufferToBase64(contentBin);
|
const content = await arrayBufferToBase64(contentBin);
|
||||||
if (content == fileOnDB.data && !force) {
|
if (isDocContentSame(content, fileOnDB.data) && !force) {
|
||||||
// Logger(`STORAGE <-- DB:${filename}: skipped (hidden) Not changed`, LOG_LEVEL_VERBOSE);
|
// Logger(`STORAGE <-- DB:${filename}: skipped (hidden) Not changed`, LOG_LEVEL_VERBOSE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await this.app.vault.adapter.writeBinary(filename, base64ToArrayBuffer(fileOnDB.data), { mtime: fileOnDB.mtime, ctime: fileOnDB.ctime });
|
await this.app.vault.adapter.writeBinary(filename, base64ToArrayBuffer(fileOnDB.data), { mtime: fileOnDB.mtime, ctime: fileOnDB.ctime });
|
||||||
try {
|
try {
|
||||||
//@ts-ignore internalAPI
|
//@ts-ignore internalAPI
|
||||||
await app.vault.adapter.reconcileInternalFile(filename);
|
await this.app.vault.adapter.reconcileInternalFile(filename);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
||||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||||
@@ -653,7 +653,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
await this.storeInternalFileToDatabase({ path: filename, ...stat }, true);
|
await this.storeInternalFileToDatabase({ path: filename, ...stat }, true);
|
||||||
try {
|
try {
|
||||||
//@ts-ignore internalAPI
|
//@ts-ignore internalAPI
|
||||||
await app.vault.adapter.reconcileInternalFile(filename);
|
await this.app.vault.adapter.reconcileInternalFile(filename);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
Logger("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
||||||
Logger(ex, LOG_LEVEL_VERBOSE);
|
Logger(ex, LOG_LEVEL_VERBOSE);
|
||||||
@@ -691,7 +691,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
const result: InternalFileInfo[] = [];
|
const result: InternalFileInfo[] = [];
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
const w = await f;
|
const w = await f;
|
||||||
if (!await this.plugin.isIgnoredByIgnoreFiles(w.path)) {
|
if (await this.plugin.isIgnoredByIgnoreFiles(w.path)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result.push({
|
result.push({
|
||||||
@@ -734,7 +734,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
if (ignoreFilter && ignoreFilter.some(e => v.match(e))) {
|
if (ignoreFilter && ignoreFilter.some(e => v.match(e))) {
|
||||||
continue L1;
|
continue L1;
|
||||||
}
|
}
|
||||||
if (!await this.plugin.isIgnoredByIgnoreFiles(v)) {
|
if (await this.plugin.isIgnoredByIgnoreFiles(v)) {
|
||||||
continue L1;
|
continue L1;
|
||||||
}
|
}
|
||||||
files = files.concat(await this.getFiles(v, ignoreList, filter, ignoreFilter));
|
files = files.concat(await this.getFiles(v, ignoreList, filter, ignoreFilter));
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
newSettingW.encryptedCouchDBConnection = "";
|
newSettingW.encryptedCouchDBConnection = "";
|
||||||
const setupJustImport = "Just import setting";
|
const setupJustImport = "Just import setting";
|
||||||
const setupAsNew = "Set it up as secondary or subsequent device";
|
const setupAsNew = "Set it up as secondary or subsequent device";
|
||||||
|
const setupAsMerge = "Secondary device but try keeping local changes";
|
||||||
const setupAgain = "Reconfigure and reconstitute the data";
|
const setupAgain = "Reconfigure and reconstitute the data";
|
||||||
const setupManually = "Leave everything to me";
|
const setupManually = "Leave everything to me";
|
||||||
newSettingW.syncInternalFiles = false;
|
newSettingW.syncInternalFiles = false;
|
||||||
@@ -108,7 +109,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
newSettingW.useIndexedDBAdapter = true;
|
newSettingW.useIndexedDBAdapter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupJustImport, setupManually]);
|
const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupAsMerge, setupJustImport, setupManually]);
|
||||||
if (setupType == setupJustImport) {
|
if (setupType == setupJustImport) {
|
||||||
this.plugin.settings = newSettingW;
|
this.plugin.settings = newSettingW;
|
||||||
this.plugin.usedPassphrase = "";
|
this.plugin.usedPassphrase = "";
|
||||||
@@ -117,6 +118,10 @@ export class SetupLiveSync extends LiveSyncCommands {
|
|||||||
this.plugin.settings = newSettingW;
|
this.plugin.settings = newSettingW;
|
||||||
this.plugin.usedPassphrase = "";
|
this.plugin.usedPassphrase = "";
|
||||||
await this.fetchLocal();
|
await this.fetchLocal();
|
||||||
|
} else if (setupType == setupAsMerge) {
|
||||||
|
this.plugin.settings = newSettingW;
|
||||||
|
this.plugin.usedPassphrase = "";
|
||||||
|
await this.fetchLocalWithKeepLocal();
|
||||||
} else if (setupType == setupAgain) {
|
} else if (setupType == setupAgain) {
|
||||||
const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
|
const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
|
||||||
if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) {
|
if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) {
|
||||||
@@ -302,13 +307,11 @@ Of course, we are able to disable these features.`
|
|||||||
Logger(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE);
|
Logger(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE);
|
||||||
this.plugin.settings.suspendParseReplicationResult = false;
|
this.plugin.settings.suspendParseReplicationResult = false;
|
||||||
this.plugin.settings.suspendFileWatching = false;
|
this.plugin.settings.suspendFileWatching = false;
|
||||||
|
await this.plugin.syncAllFiles(true);
|
||||||
|
await this.plugin.loadQueuedFiles();
|
||||||
|
this.plugin.procQueuedFiles();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
if (this.plugin.settings.readChunksOnline) {
|
|
||||||
await this.plugin.syncAllFiles(true);
|
|
||||||
await this.plugin.loadQueuedFiles();
|
|
||||||
// Start processing
|
|
||||||
this.plugin.procQueuedFiles();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
async askUseNewAdapter() {
|
async askUseNewAdapter() {
|
||||||
if (!this.plugin.settings.useIndexedDBAdapter) {
|
if (!this.plugin.settings.useIndexedDBAdapter) {
|
||||||
@@ -353,6 +356,25 @@ Of course, we are able to disable these features.`
|
|||||||
await this.resumeReflectingDatabase();
|
await this.resumeReflectingDatabase();
|
||||||
await this.askHiddenFileConfiguration({ enableFetch: true });
|
await this.askHiddenFileConfiguration({ enableFetch: true });
|
||||||
}
|
}
|
||||||
|
async fetchLocalWithKeepLocal() {
|
||||||
|
this.suspendExtraSync();
|
||||||
|
this.askUseNewAdapter();
|
||||||
|
await this.suspendReflectingDatabase();
|
||||||
|
await this.plugin.realizeSettingSyncMode();
|
||||||
|
await this.plugin.resetLocalDatabase();
|
||||||
|
await delay(1000);
|
||||||
|
await this.plugin.initializeDatabase(true);
|
||||||
|
await this.plugin.markRemoteResolved();
|
||||||
|
await this.plugin.openDatabase();
|
||||||
|
this.plugin.isReady = true;
|
||||||
|
await delay(500);
|
||||||
|
await this.plugin.replicateAllFromServer(true);
|
||||||
|
await delay(1000);
|
||||||
|
await this.plugin.replicateAllFromServer(true);
|
||||||
|
await this.fetchRemoteChunks();
|
||||||
|
await this.resumeReflectingDatabase();
|
||||||
|
await this.askHiddenFileConfiguration({ enableFetch: true });
|
||||||
|
}
|
||||||
async rebuildRemote() {
|
async rebuildRemote() {
|
||||||
this.suspendExtraSync();
|
this.suspendExtraSync();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.realizeSettingSyncMode();
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: 6efd115e0e...70eb916288
68
src/main.ts
68
src/main.ts
@@ -1167,17 +1167,25 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
if (docEntry._deleted || docEntry.deleted) {
|
if (docEntry._deleted || docEntry.deleted) {
|
||||||
// This occurs not only when files are deleted, but also when conflicts are resolved.
|
// This occurs not only when files are deleted, but also when conflicts are resolved.
|
||||||
// We have to check no other revisions are left.
|
// We have to check no other revisions are left.
|
||||||
const lastDocs = await this.localDatabase.getDBEntry(path);
|
const existDoc = await this.localDatabase.getDBEntry(path, { conflicts: true });
|
||||||
if (path != file.path) {
|
if (path != file.path) {
|
||||||
Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE);
|
Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE);
|
||||||
}
|
}
|
||||||
if (lastDocs === false) {
|
if (existDoc === false) {
|
||||||
await this.deleteVaultItem(file);
|
await this.deleteVaultItem(file);
|
||||||
} else {
|
} else {
|
||||||
// it perhaps delete some revisions.
|
if (existDoc._conflicts) {
|
||||||
// may be we have to reload this
|
if (this.settings.writeDocumentsIfConflicted) {
|
||||||
await this.pullFile(path, null, true);
|
Logger(`Delete: ${file.path}: Conflicted revision has been deleted, but there were more conflicts. `, LOG_LEVEL_INFO);
|
||||||
Logger(`delete skipped:${file.path}`, LOG_LEVEL_VERBOSE);
|
await this.pullFile(path, null, true);
|
||||||
|
} else {
|
||||||
|
Logger(`Delete: ${file.path}: Conflicted revision has been deleted, but there were more conflicts...`);
|
||||||
|
this.queueConflictedOnlyActiveFile(file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger(`Delete: ${file.path}: Conflict revision has been deleted and resolved`);
|
||||||
|
await this.pullFile(path, null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1274,6 +1282,19 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
this.dbChangeProcRunning = false;
|
this.dbChangeProcRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
queueConflictedOnlyActiveFile(file: TFile) {
|
||||||
|
if (!this.settings.checkConflictOnlyOnOpen) {
|
||||||
|
this.queueConflictedCheck(file);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
const af = this.app.workspace.getActiveFile();
|
||||||
|
if (af && af.path == file.path) {
|
||||||
|
this.queueConflictedCheck(file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
async handleDBChangedAsync(change: EntryBody) {
|
async handleDBChangedAsync(change: EntryBody) {
|
||||||
|
|
||||||
const targetFile = getAbstractFileByPath(this.getPathWithoutPrefix(change));
|
const targetFile = getAbstractFileByPath(this.getPathWithoutPrefix(change));
|
||||||
@@ -1286,29 +1307,16 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
|||||||
} else if (targetFile instanceof TFile) {
|
} else if (targetFile instanceof TFile) {
|
||||||
const doc = change;
|
const doc = change;
|
||||||
const file = targetFile;
|
const file = targetFile;
|
||||||
const queueConflictCheck = () => {
|
|
||||||
if (!this.settings.checkConflictOnlyOnOpen) {
|
|
||||||
this.queueConflictedCheck(file);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
const af = app.workspace.getActiveFile();
|
|
||||||
if (af && af.path == file.path) {
|
|
||||||
this.queueConflictedCheck(file);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this.settings.writeDocumentsIfConflicted) {
|
if (this.settings.writeDocumentsIfConflicted) {
|
||||||
await this.doc2storage(doc, file);
|
await this.doc2storage(doc, file);
|
||||||
queueConflictCheck();
|
this.queueConflictedOnlyActiveFile(file);
|
||||||
} else {
|
} else {
|
||||||
const d = await this.localDatabase.getDBEntryMeta(this.getPath(change), { conflicts: true }, true);
|
const d = await this.localDatabase.getDBEntryMeta(this.getPath(change), { conflicts: true }, true);
|
||||||
if (d && !d._conflicts) {
|
if (d && !d._conflicts) {
|
||||||
await this.doc2storage(doc, file);
|
await this.doc2storage(doc, file);
|
||||||
} else {
|
} else {
|
||||||
if (!queueConflictCheck()) {
|
if (!this.queueConflictedOnlyActiveFile(file)) {
|
||||||
Logger(`${this.getPath(change)} is conflicted, write to the storage has been pended.`, LOG_LEVEL_NOTICE);
|
Logger(`${this.getPath(change)} is conflicted, write to the storage has been postponed.`, LOG_LEVEL_NOTICE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1901,6 +1909,9 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
const ret = [] as Diff[];
|
const ret = [] as Diff[];
|
||||||
do {
|
do {
|
||||||
const d = src.shift();
|
const d = src.shift();
|
||||||
|
if (d === undefined) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
const pieces = d[1].split(/([^\n]*\n)/).filter(f => f != "");
|
const pieces = d[1].split(/([^\n]*\n)/).filter(f => f != "");
|
||||||
if (typeof (d) == "undefined") {
|
if (typeof (d) == "undefined") {
|
||||||
break;
|
break;
|
||||||
@@ -2569,9 +2580,14 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the file is ignored by the ignore files.
|
||||||
|
* @param file
|
||||||
|
* @returns true if the file should be ignored, false if the file should be processed.
|
||||||
|
*/
|
||||||
async isIgnoredByIgnoreFiles(file: string | TAbstractFile) {
|
async isIgnoredByIgnoreFiles(file: string | TAbstractFile) {
|
||||||
if (!this.settings.useIgnoreFiles) {
|
if (!this.settings.useIgnoreFiles) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
const filepath = file instanceof TFile ? file.path : file as string;
|
const filepath = file instanceof TFile ? file.path : file as string;
|
||||||
if (this.ignoreFileCache.has(filepath)) {
|
if (this.ignoreFileCache.has(filepath)) {
|
||||||
@@ -2579,14 +2595,14 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
await this.readIgnoreFile(filepath);
|
await this.readIgnoreFile(filepath);
|
||||||
}
|
}
|
||||||
if (!await isAcceptedAll(stripAllPrefixes(filepath as FilePathWithPrefix), this.ignoreFiles, (filename) => this.getIgnoreFile(filename))) {
|
if (!await isAcceptedAll(stripAllPrefixes(filepath as FilePathWithPrefix), this.ignoreFiles, (filename) => this.getIgnoreFile(filename))) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async isTargetFile(file: string | TAbstractFile) {
|
async isTargetFile(file: string | TAbstractFile) {
|
||||||
const filepath = file instanceof TFile ? file.path : file as string;
|
const filepath = file instanceof TFile ? file.path : file as string;
|
||||||
if (this.settings.useIgnoreFiles && !await this.isIgnoredByIgnoreFiles(file)) {
|
if (this.settings.useIgnoreFiles && await this.isIgnoredByIgnoreFiles(file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.localDatabase.isTargetFile(filepath);
|
return this.localDatabase.isTargetFile(filepath);
|
||||||
|
|||||||
@@ -477,7 +477,7 @@ export const requestToCouchDB = async (baseUri: string, username: string, passwo
|
|||||||
|
|
||||||
export async function performRebuildDB(plugin: ObsidianLiveSyncPlugin, method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice") {
|
export async function performRebuildDB(plugin: ObsidianLiveSyncPlugin, method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice") {
|
||||||
if (method == "localOnly") {
|
if (method == "localOnly") {
|
||||||
await plugin.addOnSetup.fetchLocal();
|
await plugin.addOnSetup.fetchLocalWithKeepLocal();
|
||||||
}
|
}
|
||||||
if (method == "remoteOnly") {
|
if (method == "remoteOnly") {
|
||||||
await plugin.addOnSetup.rebuildRemote();
|
await plugin.addOnSetup.rebuildRemote();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// "importsNotUsedAsValues": "error",
|
// "importsNotUsedAsValues": "error",
|
||||||
"importHelpers": false,
|
"importHelpers": false,
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2018",
|
"es2018",
|
||||||
"DOM",
|
"DOM",
|
||||||
|
|||||||
49
updates.md
49
updates.md
@@ -14,38 +14,7 @@ I hope you will give it a try.
|
|||||||
|
|
||||||
#### Minors
|
#### Minors
|
||||||
|
|
||||||
- 0.19.1 to 0.19.11 has been moved into the updates_old.md
|
- 0.19.1 to 0.19.14 has been moved into the updates_old.md
|
||||||
- 0.19.12
|
|
||||||
- Improved:
|
|
||||||
- Boot-up performance has been improved.
|
|
||||||
- Customisation sync performance has been improved.
|
|
||||||
- Synchronising performance has been improved.
|
|
||||||
- 0.19.13
|
|
||||||
- Implemented:
|
|
||||||
- Database clean-up is now in beta 2!
|
|
||||||
We can shrink the remote database by deleting unused chunks, with keeping history.
|
|
||||||
Note: Local database is not cleaned up totally. We have to `Fetch` again to let it done.
|
|
||||||
**Note2**: Still in beta. Please back your vault up anything before.
|
|
||||||
- Fixed:
|
|
||||||
- The log updates are not thinned out now.
|
|
||||||
- 0.19.14
|
|
||||||
- Fixed:
|
|
||||||
- Internal documents are now ignored.
|
|
||||||
- Merge dialogue now respond immediately to button pressing.
|
|
||||||
- Periodic processing now works fine.
|
|
||||||
- The checking interval of detecting conflicted has got shorter.
|
|
||||||
- Replication is now cancelled while cleaning up.
|
|
||||||
- The database locking by the cleaning up is now carefully unlocked.
|
|
||||||
- Missing chunks message is correctly reported.
|
|
||||||
- New feature:
|
|
||||||
- Suspend database reflecting has been implemented.
|
|
||||||
- This can be disabled by `Fetch database with previous behaviour`.
|
|
||||||
- Now fetch suspends the reflecting database and storage changes temporarily to improve the performance.
|
|
||||||
- We can choose the action when the remote database has been cleaned
|
|
||||||
- Merge dialogue now show `↲` before the new line.
|
|
||||||
- Improved:
|
|
||||||
- Now progress is reported while the cleaning up and fetch process.
|
|
||||||
- Cancelled replication is now detected.
|
|
||||||
- 0.19.15
|
- 0.19.15
|
||||||
- Fixed:
|
- Fixed:
|
||||||
- Now storing files after cleaning up is correct works.
|
- Now storing files after cleaning up is correct works.
|
||||||
@@ -67,5 +36,21 @@ I hope you will give it a try.
|
|||||||
- Compiler, framework, and dependencies have been upgraded.
|
- Compiler, framework, and dependencies have been upgraded.
|
||||||
- Due to standing for these impacts (especially in esbuild and svelte,) terser has been introduced.
|
- Due to standing for these impacts (especially in esbuild and svelte,) terser has been introduced.
|
||||||
Feel free to notify your opinion to me! I do not like to obfuscate the code too.
|
Feel free to notify your opinion to me! I do not like to obfuscate the code too.
|
||||||
|
- 0.19.17
|
||||||
|
- Fixed:
|
||||||
|
- Now nested ignore files could be parsed correctly.
|
||||||
|
- The unexpected deletion of hidden files in some cases has been corrected.
|
||||||
|
- Hidden file change is no longer reflected on the device which has made the change itself.
|
||||||
|
- Behaviour changed:
|
||||||
|
- From this version, the file which has `:` in its name should be ignored even if on Linux devices.
|
||||||
|
- 0.19.18
|
||||||
|
- Fixed:
|
||||||
|
- Now the empty (or deleted) file could be conflict-resolved.
|
||||||
|
- 0.19.19
|
||||||
|
- Fixed:
|
||||||
|
- Resolving conflicted revision has become more robust.
|
||||||
|
- LiveSync now try to keep local changes when fetching from the rebuilt remote database.
|
||||||
|
Local changes now have been kept as a revision and fetched things will be new revisions.
|
||||||
|
- Now, all files will be restored after performing `fetch` immediately.
|
||||||
|
|
||||||
... To continue on to `updates_old.md`.
|
... To continue on to `updates_old.md`.
|
||||||
|
|||||||
@@ -82,6 +82,37 @@ I hope you will give it a try.
|
|||||||
- Logging keeps 400 lines now.
|
- Logging keeps 400 lines now.
|
||||||
- Refactored:
|
- Refactored:
|
||||||
- Import statement has been fixed about types.
|
- Import statement has been fixed about types.
|
||||||
|
- 0.19.12
|
||||||
|
- Improved:
|
||||||
|
- Boot-up performance has been improved.
|
||||||
|
- Customisation sync performance has been improved.
|
||||||
|
- Synchronising performance has been improved.
|
||||||
|
- 0.19.13
|
||||||
|
- Implemented:
|
||||||
|
- Database clean-up is now in beta 2!
|
||||||
|
We can shrink the remote database by deleting unused chunks, with keeping history.
|
||||||
|
Note: Local database is not cleaned up totally. We have to `Fetch` again to let it done.
|
||||||
|
**Note2**: Still in beta. Please back your vault up anything before.
|
||||||
|
- Fixed:
|
||||||
|
- The log updates are not thinned out now.
|
||||||
|
- 0.19.14
|
||||||
|
- Fixed:
|
||||||
|
- Internal documents are now ignored.
|
||||||
|
- Merge dialogue now respond immediately to button pressing.
|
||||||
|
- Periodic processing now works fine.
|
||||||
|
- The checking interval of detecting conflicted has got shorter.
|
||||||
|
- Replication is now cancelled while cleaning up.
|
||||||
|
- The database locking by the cleaning up is now carefully unlocked.
|
||||||
|
- Missing chunks message is correctly reported.
|
||||||
|
- New feature:
|
||||||
|
- Suspend database reflecting has been implemented.
|
||||||
|
- This can be disabled by `Fetch database with previous behaviour`.
|
||||||
|
- Now fetch suspends the reflecting database and storage changes temporarily to improve the performance.
|
||||||
|
- We can choose the action when the remote database has been cleaned
|
||||||
|
- Merge dialogue now show `↲` before the new line.
|
||||||
|
- Improved:
|
||||||
|
- Now progress is reported while the cleaning up and fetch process.
|
||||||
|
- Cancelled replication is now detected.
|
||||||
### 0.18.0
|
### 0.18.0
|
||||||
|
|
||||||
#### Now, paths of files in the database can now be obfuscated. (Experimental Feature)
|
#### Now, paths of files in the database can now be obfuscated. (Experimental Feature)
|
||||||
|
|||||||
Reference in New Issue
Block a user