/
raiden
/
obsidian-git-encrypt
Обзор
Документация
Войти
/
raiden
/
obsidian-git-encrypt
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/settings/settings.ts
111 строк
4 KB
Robert Kuzhin
feat: add manual encrypted source control
09 авг 2026, 20:57
09 авг 2026, 20:57
3c8fa38
Код
Авторство
О чём код?
import path from "node:path"; export interface EncryptedGitSettings { readonly mirrorPath: string; readonly statePath: string; readonly remoteUrl: string; readonly branch: string; readonly backgroundFetch: boolean; readonly backgroundFetchIntervalMinutes: number; readonly commitMessage: string; } export function defaultSettings(vaultRoot: string): EncryptedGitSettings { const parent = path.dirname(vaultRoot); const vaultName = path.basename(vaultRoot); return { mirrorPath: path.join(parent, `${vaultName}-encrypted-repository`), statePath: path.join(vaultRoot, ".obsidian", "plugins", "encrypted-git", "local-state"), remoteUrl: "", branch: "main", backgroundFetch: false, backgroundFetchIntervalMinutes: 15, commitMessage: "Encrypted vault update", }; } export function loadSettings(input: unknown, vaultRoot: string): EncryptedGitSettings { const defaults = defaultSettings(vaultRoot); if (!isRecord(input)) return defaults; return { mirrorPath: validAbsolutePath(input.mirrorPath) ?? defaults.mirrorPath, statePath: validAbsolutePath(input.statePath) ?? defaults.statePath, remoteUrl: validRemoteUrl(input.remoteUrl) ?? defaults.remoteUrl, branch: validBranch(input.branch) ?? defaults.branch, backgroundFetch: typeof input.backgroundFetch === "boolean" ? input.backgroundFetch : defaults.backgroundFetch, backgroundFetchIntervalMinutes: typeof input.backgroundFetchIntervalMinutes === "number" && Number.isSafeInteger(input.backgroundFetchIntervalMinutes) && input.backgroundFetchIntervalMinutes >= 1 && input.backgroundFetchIntervalMinutes <= 1_440 ? input.backgroundFetchIntervalMinutes : defaults.backgroundFetchIntervalMinutes, commitMessage: typeof input.commitMessage === "string" && input.commitMessage.length > 0 && input.commitMessage.length <= 200 && !/[\0\r\n]/u.test(input.commitMessage) ? input.commitMessage : defaults.commitMessage, }; } export function validateAbsolutePath(value: string): string | undefined { if (value.length === 0) return "Path is required."; if (!path.isAbsolute(value)) return "Path must be absolute."; if (value.includes("\0")) return "Path contains a forbidden character."; return undefined; } export function validateRemoteSetting(value: string): string | undefined { if (value === "") return undefined; if (value.length > 8_192 || value.startsWith("-") || /[\0\r\n]/u.test(value)) { return "Remote URL is invalid."; } if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(value)) { try { const parsed = new URL(value); if (parsed.password.length > 0 || parsed.search.length > 0) { return "Use a credential helper or SSH agent; do not embed credentials or query tokens."; } } catch { return "Remote URL is invalid."; } } return undefined; } export function validateBranchSetting(value: string): string | undefined { return validBranch(value) === undefined ? "Branch name is invalid." : undefined; } function validAbsolutePath(value: unknown): string | undefined { return typeof value === "string" && validateAbsolutePath(value) === undefined ? value : undefined; } function validRemoteUrl(value: unknown): string | undefined { return typeof value === "string" && validateRemoteSetting(value) === undefined ? value : undefined; } function validBranch(value: unknown): string | undefined { if ( typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$/u.test(value) || value.includes("..") || value.includes("//") || value.includes("@{") || value.endsWith("/") || value.endsWith(".") || value.endsWith(".lock") ) { return undefined; } return value; } function isRecord(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }