-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsecure-webdav-sync-support.ts
More file actions
116 lines (98 loc) · 3.51 KB
/
secure-webdav-sync-support.ts
File metadata and controls
116 lines (98 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { App, TFile, TFolder, normalizePath } from "obsidian";
export type RemoteFileLike = {
remotePath: string;
lastModified: number;
size: number;
signature: string;
};
type SecureWebdavSyncSupportDeps = {
app: App;
getVaultSyncRemoteFolder: () => string;
getExcludedSyncFolders?: () => string[];
encodeBase64Url: (value: string) => string;
decodeBase64Url: (value: string) => string;
};
// Keep sync metadata rules isolated so queue/rendering changes
// do not accidentally affect reconciliation behaviour.
export class SecureWebdavSyncSupport {
constructor(private readonly deps: SecureWebdavSyncSupportDeps) {}
isExcludedSyncPath(path: string) {
const normalizedPath = normalizePath(path).replace(/^\/+/, "").replace(/\/+$/, "");
if (!normalizedPath) {
return false;
}
const folders = this.deps.getExcludedSyncFolders?.() ?? [];
return folders.some((folder) => {
const normalizedFolder = normalizePath(folder).replace(/^\/+/, "").replace(/\/+$/, "");
return normalizedFolder.length > 0 && (normalizedPath === normalizedFolder || normalizedPath.startsWith(`${normalizedFolder}/`));
});
}
shouldSkipContentSyncPath(path: string) {
const normalizedPath = normalizePath(path);
if (
this.isExcludedSyncPath(normalizedPath) ||
normalizedPath.startsWith(".obsidian/") ||
normalizedPath.startsWith(".trash/") ||
normalizedPath.startsWith(".git/") ||
normalizedPath.startsWith("node_modules/") ||
normalizedPath.startsWith("_plugin_packages/") ||
normalizedPath.startsWith(".tmp-") ||
normalizedPath.startsWith(".obsidian/plugins/secure-webdav-images/")
) {
return true;
}
return /\.(png|jpe?g|gif|webp|bmp|svg)$/i.test(normalizedPath);
}
shouldSkipDirectorySyncPath(dirPath: string) {
const p = normalizePath(dirPath);
return (
this.isExcludedSyncPath(p) ||
p.startsWith(".obsidian") ||
p.startsWith(".trash") ||
p.startsWith(".git") ||
p.startsWith("node_modules") ||
p.startsWith("_plugin_packages") ||
p.startsWith(".tmp-")
);
}
collectLocalSyncedDirectories() {
const dirs = new Set<string>();
for (const f of this.deps.app.vault.getAllFolders()) {
if (f instanceof TFolder && !f.isRoot() && !this.shouldSkipDirectorySyncPath(f.path)) {
dirs.add(normalizePath(f.path));
}
}
return dirs;
}
collectVaultContentFiles() {
return this.deps.app.vault
.getFiles()
.filter((file) => !this.shouldSkipContentSyncPath(file.path))
.sort((a, b) => a.path.localeCompare(b.path));
}
buildSyncSignature(file: TFile) {
return `${file.stat.mtime}:${file.stat.size}`;
}
buildRemoteSyncSignature(remote: Pick<RemoteFileLike, "lastModified" | "size">) {
return `${remote.lastModified}:${remote.size}`;
}
buildVaultSyncRemotePath(vaultPath: string) {
return `${this.normalizeFolder(this.deps.getVaultSyncRemoteFolder())}${vaultPath}`;
}
remotePathToVaultPath(remotePath: string) {
const root = this.normalizeFolder(this.deps.getVaultSyncRemoteFolder());
if (!remotePath.startsWith(root)) {
return null;
}
return remotePath.slice(root.length).replace(/^\/+/, "");
}
shouldDownloadRemoteVersion(localMtime: number, remoteMtime: number) {
return remoteMtime > localMtime + 2000;
}
private normalizeFolder(input: string) {
return normalizeFolder(input);
}
}
export function normalizeFolder(input: string) {
return input.replace(/^\/+/, "").replace(/\/+$/, "") + "/";
}