mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-01-17 18:26:11 +02:00
Refactored:
- PouchDB handling moved into Common lib.
This commit is contained in:
parent
b14ecdb205
commit
83209f3923
1
pouchdb-browser-webpack/.gitignore
vendored
1
pouchdb-browser-webpack/.gitignore
vendored
@ -1 +0,0 @@
|
||||
node_modules
|
@ -1,4 +0,0 @@
|
||||
# PouchDB-browser
|
||||
|
||||
Just webpacked.
|
||||
(Rollup couldn't pack pouchdb-browser into browser bundle)
|
File diff suppressed because one or more lines are too long
@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "pouchdb-browser-webpack",
|
||||
"version": "1.0.0",
|
||||
"description": "pouchdb-browser webpack",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack --mode=production --node-env=production",
|
||||
"build:dev": "webpack --mode=development",
|
||||
"build:prod": "webpack --mode=production --node-env=production",
|
||||
"watch": "webpack --watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"pouchdb-browser": "^7.3.0",
|
||||
"transform-pouch": "^2.0.0",
|
||||
"pouchdb-find": "^7.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"webpack": "^5.58.1",
|
||||
"webpack-cli": "^4.9.0"
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/* eslint-disable no-undef */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// This module just webpacks pouchdb-browser
|
||||
// import * as PouchDB_src from "pouchdb-browser";
|
||||
const pouch = require("pouchdb-browser").default;
|
||||
const find = require("pouchdb-find").default;
|
||||
const transform = require("transform-pouch");
|
||||
const PouchDB = pouch.plugin(find).plugin(transform);
|
||||
|
||||
export { PouchDB };
|
@ -1,30 +0,0 @@
|
||||
// Generated using webpack-cli https://github.com/webpack/webpack-cli
|
||||
|
||||
const path = require("path");
|
||||
|
||||
const isProduction = process.env.NODE_ENV == "production";
|
||||
|
||||
const config = {
|
||||
entry: "./src/index.js",
|
||||
output: {
|
||||
filename: "pouchdb-browser.js",
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
library: {
|
||||
type: "module",
|
||||
},
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true,
|
||||
},
|
||||
plugins: [],
|
||||
module: {},
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
if (isProduction) {
|
||||
config.mode = "production";
|
||||
} else {
|
||||
config.mode = "development";
|
||||
}
|
||||
return config;
|
||||
};
|
1481
src/LocalPouchDB.ts
1481
src/LocalPouchDB.ts
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ import { EntryDoc, LOG_LEVEL, RemoteDBSettings } from "./lib/src/types";
|
||||
import { path2id, id2path } from "./utils";
|
||||
import { delay, runWithLock, versionNumberString2Number } from "./lib/src/utils";
|
||||
import { Logger } from "./lib/src/logger";
|
||||
import { checkSyncInfo, connectRemoteCouchDBWithSetting } from "./utils_couchdb";
|
||||
import { checkSyncInfo } from "./lib/src/utils_couchdb";
|
||||
import { testCrypt } from "./lib/src/e2ee_v2";
|
||||
import ObsidianLiveSyncPlugin from "./main";
|
||||
|
||||
@ -15,7 +15,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
async testConnection(): Promise<void> {
|
||||
const db = await connectRemoteCouchDBWithSetting(this.plugin.settings, this.plugin.localDatabase.isMobile);
|
||||
const db = await this.plugin.localDatabase.connectRemoteCouchDBWithSetting(this.plugin.settings, this.plugin.localDatabase.isMobile);
|
||||
if (typeof db === "string") {
|
||||
this.plugin.addLog(`could not connect to ${this.plugin.settings.couchDB_URI} : ${this.plugin.settings.couchDB_DBNAME} \n(${db})`, LOG_LEVEL.NOTICE);
|
||||
return;
|
||||
@ -225,7 +225,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
passphrase: this.plugin.settings.workingPassphrase,
|
||||
};
|
||||
console.dir(settingForCheck);
|
||||
const db = await connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.localDatabase.isMobile);
|
||||
const db = await this.plugin.localDatabase.connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.localDatabase.isMobile);
|
||||
if (typeof db === "string") {
|
||||
Logger("Could not connect to the database.", LOG_LEVEL.NOTICE);
|
||||
return false;
|
||||
|
2
src/lib
2
src/lib
@ -1 +1 @@
|
||||
Subproject commit aacfa353a9cc9f8af01023503c4ea3dee74f7f8f
|
||||
Subproject commit 57db68352b7d891a29529eb2b26ef4fcbb29bc76
|
@ -1,4 +0,0 @@
|
||||
import { PouchDB as PouchDB_ } from "../pouchdb-browser-webpack/dist/pouchdb-browser.js";
|
||||
|
||||
const Pouch: PouchDB.Static = PouchDB_;
|
||||
export { Pouch as PouchDB };
|
@ -1,279 +0,0 @@
|
||||
import { Logger } from "./lib/src/logger";
|
||||
import { LOG_LEVEL, VER, VERSIONINFO_DOCID, EntryVersionInfo, EntryDoc, RemoteDBSettings, SYNCINFO_ID, SyncInfo } from "./lib/src/types";
|
||||
import { enableEncryption, resolveWithIgnoreKnownError } from "./lib/src/utils";
|
||||
import { PouchDB } from "./pouchdb-browser";
|
||||
import { requestUrl, RequestUrlParam, RequestUrlResponse } from "obsidian";
|
||||
|
||||
export const isValidRemoteCouchDBURI = (uri: string): boolean => {
|
||||
if (uri.startsWith("https://")) return true;
|
||||
if (uri.startsWith("http://")) return true;
|
||||
return false;
|
||||
};
|
||||
let last_successful_post = false;
|
||||
export const getLastPostFailedBySize = () => {
|
||||
return !last_successful_post;
|
||||
};
|
||||
const fetchByAPI = async (request: RequestUrlParam): Promise<RequestUrlResponse> => {
|
||||
const ret = await requestUrl(request);
|
||||
if (ret.status - (ret.status % 100) !== 200) {
|
||||
const er: Error & { status?: number } = new Error(`Request Error:${ret.status}`);
|
||||
if (ret.json) {
|
||||
er.message = ret.json.reason;
|
||||
er.name = `${ret.json.error ?? ""}:${ret.json.message ?? ""}`;
|
||||
}
|
||||
er.status = ret.status;
|
||||
throw er;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const connectRemoteCouchDBWithSetting = (settings: RemoteDBSettings, isMobile: boolean) =>
|
||||
connectRemoteCouchDB(
|
||||
settings.couchDB_URI + (settings.couchDB_DBNAME == "" ? "" : "/" + settings.couchDB_DBNAME),
|
||||
{
|
||||
username: settings.couchDB_USER,
|
||||
password: settings.couchDB_PASSWORD,
|
||||
},
|
||||
settings.disableRequestURI || isMobile,
|
||||
settings.encrypt ? settings.passphrase : settings.encrypt
|
||||
);
|
||||
|
||||
const connectRemoteCouchDB = async (uri: string, auth: { username: string; password: string }, disableRequestURI: boolean, passphrase: string | boolean): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> => {
|
||||
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.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
||||
let authHeader = "";
|
||||
if (auth.username && auth.password) {
|
||||
const utf8str = String.fromCharCode.apply(null, new TextEncoder().encode(`${auth.username}:${auth.password}`));
|
||||
const encoded = window.btoa(utf8str);
|
||||
authHeader = "Basic " + encoded;
|
||||
} else {
|
||||
authHeader = "";
|
||||
}
|
||||
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
||||
adapter: "http",
|
||||
auth,
|
||||
fetch: async function (url: string | Request, opts: RequestInit) {
|
||||
let size = "";
|
||||
const localURL = url.toString().substring(uri.length);
|
||||
const method = opts.method ?? "GET";
|
||||
if (opts.body) {
|
||||
const opts_length = opts.body.toString().length;
|
||||
if (opts_length > 1024 * 1024 * 10) {
|
||||
// over 10MB
|
||||
if (uri.contains(".cloudantnosqldb.")) {
|
||||
last_successful_post = false;
|
||||
Logger("This request should fail on IBM Cloudant.", LOG_LEVEL.VERBOSE);
|
||||
throw new Error("This request should fail on IBM Cloudant.");
|
||||
}
|
||||
}
|
||||
size = ` (${opts_length})`;
|
||||
}
|
||||
|
||||
if (!disableRequestURI && typeof url == "string" && typeof (opts.body ?? "") == "string") {
|
||||
const body = opts.body as string;
|
||||
|
||||
const transformedHeaders = { ...(opts.headers as Record<string, string>) };
|
||||
if (authHeader != "") transformedHeaders["authorization"] = authHeader;
|
||||
delete transformedHeaders["host"];
|
||||
delete transformedHeaders["Host"];
|
||||
delete transformedHeaders["content-length"];
|
||||
delete transformedHeaders["Content-Length"];
|
||||
const requestParam: RequestUrlParam = {
|
||||
url: url as string,
|
||||
method: opts.method,
|
||||
body: body,
|
||||
headers: transformedHeaders,
|
||||
contentType: "application/json",
|
||||
// contentType: opts.headers,
|
||||
};
|
||||
|
||||
try {
|
||||
const r = await fetchByAPI(requestParam);
|
||||
if (method == "POST" || method == "PUT") {
|
||||
last_successful_post = r.status - (r.status % 100) == 200;
|
||||
} else {
|
||||
last_successful_post = true;
|
||||
}
|
||||
Logger(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL.DEBUG);
|
||||
|
||||
return new Response(r.arrayBuffer, {
|
||||
headers: r.headers,
|
||||
status: r.status,
|
||||
statusText: `${r.status}`,
|
||||
});
|
||||
} catch (ex) {
|
||||
Logger(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL.VERBOSE);
|
||||
// limit only in bulk_docs.
|
||||
if (url.toString().indexOf("_bulk_docs") !== -1) {
|
||||
last_successful_post = false;
|
||||
}
|
||||
Logger(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
// -old implementation
|
||||
|
||||
try {
|
||||
const response: Response = await fetch(url, opts);
|
||||
if (method == "POST" || method == "PUT") {
|
||||
last_successful_post = response.ok;
|
||||
} else {
|
||||
last_successful_post = true;
|
||||
}
|
||||
Logger(`HTTP:${method}${size} to:${localURL} -> ${response.status}`, LOG_LEVEL.DEBUG);
|
||||
return response;
|
||||
} catch (ex) {
|
||||
Logger(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL.VERBOSE);
|
||||
// limit only in bulk_docs.
|
||||
if (url.toString().indexOf("_bulk_docs") !== -1) {
|
||||
last_successful_post = false;
|
||||
}
|
||||
Logger(ex);
|
||||
throw ex;
|
||||
}
|
||||
// return await fetch(url, opts);
|
||||
},
|
||||
};
|
||||
|
||||
const db: PouchDB.Database<EntryDoc> = new PouchDB<EntryDoc>(uri, conf);
|
||||
if (passphrase && typeof passphrase === "string") {
|
||||
enableEncryption(db, passphrase);
|
||||
}
|
||||
try {
|
||||
const info = await db.info();
|
||||
return { db: db, info: info };
|
||||
} catch (ex) {
|
||||
let msg = `${ex.name}:${ex.message}`;
|
||||
if (ex.name == "TypeError" && ex.message == "Failed to fetch") {
|
||||
msg += "\n**Note** This error caused by many reasons. The only sure thing is you didn't touch the server.\nTo check details, open inspector.";
|
||||
}
|
||||
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
// check the version of remote.
|
||||
// if remote is higher than current(or specified) version, return false.
|
||||
export const checkRemoteVersion = async (db: PouchDB.Database, migrate: (from: number, to: number) => Promise<boolean>, barrier: number = VER): Promise<boolean> => {
|
||||
try {
|
||||
const versionInfo = (await db.get(VERSIONINFO_DOCID)) as EntryVersionInfo;
|
||||
if (versionInfo.type != "versioninfo") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const version = versionInfo.version;
|
||||
if (version < barrier) {
|
||||
const versionUpResult = await migrate(version, barrier);
|
||||
if (versionUpResult) {
|
||||
await bumpRemoteVersion(db);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (version == barrier) return true;
|
||||
return false;
|
||||
} catch (ex) {
|
||||
if (ex.status && ex.status == 404) {
|
||||
if (await bumpRemoteVersion(db)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
};
|
||||
export const bumpRemoteVersion = async (db: PouchDB.Database, barrier: number = VER): Promise<boolean> => {
|
||||
const vi: EntryVersionInfo = {
|
||||
_id: VERSIONINFO_DOCID,
|
||||
version: barrier,
|
||||
type: "versioninfo",
|
||||
};
|
||||
const versionInfo = (await resolveWithIgnoreKnownError(db.get(VERSIONINFO_DOCID), vi)) as EntryVersionInfo;
|
||||
if (versionInfo.type != "versioninfo") {
|
||||
return false;
|
||||
}
|
||||
vi._rev = versionInfo._rev;
|
||||
await db.put(vi);
|
||||
return true;
|
||||
};
|
||||
|
||||
export const checkSyncInfo = async (db: PouchDB.Database): Promise<boolean> => {
|
||||
try {
|
||||
const syncinfo = (await db.get(SYNCINFO_ID)) as SyncInfo;
|
||||
console.log(syncinfo);
|
||||
// if we could decrypt the doc, it must be ok.
|
||||
return true;
|
||||
} catch (ex) {
|
||||
if (ex.status && ex.status == 404) {
|
||||
const randomStrSrc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const temp = [...Array(30)]
|
||||
.map((e) => Math.floor(Math.random() * randomStrSrc.length))
|
||||
.map((e) => randomStrSrc[e])
|
||||
.join("");
|
||||
const newSyncInfo: SyncInfo = {
|
||||
_id: SYNCINFO_ID,
|
||||
type: "syncinfo",
|
||||
data: temp,
|
||||
};
|
||||
if (await db.put(newSyncInfo)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
console.dir(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export async function putDesignDocuments(db: PouchDB.Database) {
|
||||
type DesignDoc = {
|
||||
_id: string;
|
||||
_rev: string;
|
||||
ver: number;
|
||||
filters: {
|
||||
default: string,
|
||||
push: string,
|
||||
pull: string,
|
||||
};
|
||||
}
|
||||
const design: DesignDoc = {
|
||||
"_id": "_design/replicate",
|
||||
"_rev": undefined as string | undefined,
|
||||
"ver": 2,
|
||||
"filters": {
|
||||
"default": function (doc: any, req: any) {
|
||||
return !("remote" in doc && doc.remote);
|
||||
}.toString(),
|
||||
"push": function (doc: any, req: any) {
|
||||
return true;
|
||||
}.toString(),
|
||||
"pull": function (doc: any, req: any) {
|
||||
return !(doc.type && doc.type == "leaf")
|
||||
}.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
// We can use the filter on replication : filter: 'replicate/default',
|
||||
|
||||
try {
|
||||
const w = await db.get<DesignDoc>(design._id);
|
||||
if (w.ver < design.ver) {
|
||||
design._rev = w._rev;
|
||||
//@ts-ignore
|
||||
await db.put(design);
|
||||
return true;
|
||||
}
|
||||
} catch (ex) {
|
||||
if (ex.status && ex.status == 404) {
|
||||
delete design._rev;
|
||||
//@ts-ignore
|
||||
await db.put(design);
|
||||
return true;
|
||||
} else {
|
||||
Logger("Could not make design documents", LOG_LEVEL.INFO);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user