From 6ee4705e2a0c3958c9524e61b42513cb185194b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yingyi=20/=20=E9=A2=96=E9=80=B8?= <49649786+Zuoqiu-Yingyi@users.noreply.github.com> Date: Tue, 31 Dec 2024 21:06:13 +0800 Subject: [PATCH] :art: Support local file system sync & backup (#13663) * :art: Use local file system sync & backup * :arrow_up: dejavu * :art: Add the settings panel of local file system sync & backup * :memo: Update user guides of local file system sync & backup * :art: Adjust supported runtime environments --- app/appearance/langs/ar_SA.json | 5 +- app/appearance/langs/de_DE.json | 3 + app/appearance/langs/en_US.json | 3 + app/appearance/langs/es_ES.json | 3 + app/appearance/langs/fr_FR.json | 3 + app/appearance/langs/he_IL.json | 3 + app/appearance/langs/it_IT.json | 3 + app/appearance/langs/ja_JP.json | 3 + app/appearance/langs/pl_PL.json | 3 + app/appearance/langs/ru_RU.json | 3 + app/appearance/langs/zh_CHT.json | 3 + app/appearance/langs/zh_CN.json | 3 + .../20230805232018-hgrq0ju.sy | 32 ++++++++- .../20230805230131-sn7obzb.sy | 32 ++++++++- .../20230805232903-erdoerp.sy | 32 ++++++++- .../20240530101000-na9sys7.sy | 32 ++++++++- app/src/config/repos.ts | 67 ++++++++++++++++++- app/src/types/config.d.ts | 24 +++++++ app/src/util/functions.ts | 14 +++- kernel/api/router.go | 1 + kernel/api/sync.go | 39 +++++++++++ kernel/conf/sync.go | 10 +++ kernel/model/conf.go | 7 ++ kernel/model/repository.go | 18 +++-- kernel/model/sync.go | 27 +++++++- kernel/util/net.go | 14 ++-- kernel/util/path.go | 53 ++++++++++++--- 27 files changed, 408 insertions(+), 32 deletions(-) diff --git a/app/appearance/langs/ar_SA.json b/app/appearance/langs/ar_SA.json index 6c4507ded37..11fa101b999 100644 --- a/app/appearance/langs/ar_SA.json +++ b/app/appearance/langs/ar_SA.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "نظام الملفات المحلي", + "deviceNotSupport": "الجهاز الحالي غير مدعوم", "second": "ثانية", "syncInterval": "الفاصل الزمني للمزامنة", "syncIntervalTip": "مزامنة البيانات تلقائياً بعد أن توقف عن التغيير", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "‫المحدد حاليا هو خدمة تخزين السحابة التي توفرها رسميا SiYuan، والتي تدعم فقط المستخدمين المشتركين المدفوعة‬", "syncThirdPartyProviderS3Intro": "‫المحدد حاليا هو خدمة تخزين سحابية تابعة لطرف ثالث متوافقة مع بروتوكول Amazon S3 (على سبيل المثال Qiniu، Aliyun OSS، Cloudflare R2)‬", "syncThirdPartyProviderWebDAVIntro": "‫المحدد حاليا هو خدمة تخزين سحابية لطرف ثالث مع بروتوكول WebDAV. بسبب ضعف أداء هذا البروتوكول وعدم كفاية استقراره، يوصى باختيار خدمة S3 أولا‬", + "syncThirdPartyProviderLocalIntro": "‫المحدد حاليا هو خدمة تخزين سحابية لطرف ثالث مع بروتوكوليتم حاليًا اختيار خدمة التخزين التي يوفرها نظام الملفات المحلي. الرجاء إدخال المسار الكامل للمجلد المحلي في حقل Endpoint‬", "syncThirdPartyProviderTip": "‫قبل استخدام هذه الميزة، يرجى الانتباه إلى القيود التقنية وبنود الفوترة لخدمة التخزين السحابي من الطرف الثالث:
إخلاء المسؤولية: للمزيد من التفاصيل، يرجى الرجوع إلى الشروط ذات الصلة في ترخيص المصدر المفتوح AGPLv3 المستخدم في SiYuan‬", "syncProvider": "موفر خدمات التخزين السحابي", "syncProviderTip": "إذا تم اختياره، سيتم استخدام خدمة التخزين السحابية لهذا الموفر لتحقيق مزامنة السحابة ووظائف النسخ الاحتياطي", @@ -1581,4 +1584,4 @@ "251": "‫مجموع الأصول غير المستخدمة [%d]، [%d] فقط منها مدرج هنا‬", "252": "\uD83D\uDEA8 قد يقوم Microsoft Defender بحذف النواة عن طريق الخطأ، وحذف البيانات وتقليل الأداء بشكل كبير. يوصى بإضافة مسار التثبيت ومسار مساحة العمل الخاصة بـ SiYuan إلى قائمة الاستثناءات" } -} \ No newline at end of file +} diff --git a/app/appearance/langs/de_DE.json b/app/appearance/langs/de_DE.json index 4ef52766909..903a417585a 100644 --- a/app/appearance/langs/de_DE.json +++ b/app/appearance/langs/de_DE.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "Lokales Dateisystem", + "deviceNotSupport": "Das aktuelle Gerät wird nicht unterstützt", "second": "Sekunde", "syncInterval": "Synchronisierungsintervall", "syncIntervalTip": "Automatische Datensynchronisierung, nachdem die Daten nicht mehr geändert werden", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "Derzeit ausgewählt ist der von SiYuan offiziell bereitgestellte Cloud-Speicherdienst, der nur abonnierte Benutzer unterstützt.", "syncThirdPartyProviderS3Intro": "Derzeit ausgewählt ist ein Drittanbieter-Cloudspeicherdienst, der mit dem Amazon S3-Protokoll kompatibel ist (z. B. Qiniu, Aliyun OSS, Cloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "Derzeit ausgewählt ist ein Drittanbieter-Cloudspeicherdienst mit dem WebDAV-Protokoll. Aufgrund der schlechten Leistung und unzureichenden Stabilität dieses Protokolls wird empfohlen, zuerst den S3-Dienst auszuwählen.", + "syncThirdPartyProviderLocalIntro": "Derzeit wird der vom lokalen Dateisystem bereitgestellte Speicherdienst ausgewählt. Bitte geben Sie im Feld Endpoint den vollständigen Pfad des lokalen Ordners ein", "syncThirdPartyProviderTip": "Bitte beachten Sie vor der Nutzung dieser Funktion die technischen Einschränkungen und Abrechnungspositionen des Drittanbieter-Cloudspeicherdienstes:
Haftungsausschluss: Für Details konsultieren Sie bitte die entsprechenden Bestimmungen in der Open Source Lizenz AGPLv3, die von SiYuan verwendet wird.", "syncProvider": "Cloud-Speicherdienstanbieter", "syncProviderTip": "Wenn ausgewählt, wird der Cloud-Speicherdienst dieses Anbieters verwendet, um Cloud-Synchronisations- und Backup-Funktionen zu erreichen.", diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index bb679a0fa1c..d3d92cf506b 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "Local file system", + "deviceNotSupport": "The current device is not supported", "second": "second", "syncInterval": "sync interval", "syncIntervalTip": "Automatically sync data after it stops changing", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "Currently selected is the cloud storage service officially provided by SiYuan, which only supports paid subscription users", "syncThirdPartyProviderS3Intro": "The current selected is a third-party cloud storage service compatible with the Amazon S3 protocol (eg Qiniu, Aliyun OSS, Cloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "The currently selected is a third-party cloud storage service with the WebDAV protocol. Due to the poor performance and insufficient stability of this protocol, it is recommended to choose the S3 service first", + "syncThirdPartyProviderLocalIntro": "The storage service provided by the local file system is currently selected. Please enter the full path of the local folder in the Endpoint field", "syncThirdPartyProviderTip": "Before using this feature, please pay attention to the technical limitations and billing items of the third-party cloud storage service:
Disclaimer: For details, please refer to the relevant terms in the open source license AGPLv3 used by SiYuan", "syncProvider": "Cloud storage service provider", "syncProviderTip": "If selected, the cloud storage service of this provider will be used to achieve cloud synchronization and backup functions", diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 6cfbc0c8fd5..c2117823ed2 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "Sistema de archivos local", + "deviceNotSupport": "El dispositivo actual no es compatible", "second": "segundo", "syncInterval": "intervalo de sincronización", "syncIntervalTip": "Sincronización automática de datos después de que los datos dejen de cambiar", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "Actualmente seleccionado es el servicio de almacenamiento en la nube proporcionado oficialmente por SiYuan, que solo admite usuarios de suscripción paga", "syncThirdPartyProviderS3Intro": "El actual seleccionado es un servicio de almacenamiento en la nube de terceros compatible con el protocolo Amazon S3", "syncThirdPartyProviderWebDAVIntro": "El servicio de almacenamiento en la nube de terceros del protocolo WebDAV está actualmente seleccionado. Debido al bajo rendimiento y la estabilidad insuficiente de este protocolo, se recomienda elegir primero el servicio S3", + "syncThirdPartyProviderLocalIntro": "Actualmente está seleccionado el servicio de almacenamiento proporcionado por el sistema de archivos local. Por favor, ingrese la ruta completa de la carpeta local en el campo Endpoint", "syncThirdPartyProviderTip": "Antes de usar esta función, preste atención a las limitaciones técnicas y los elementos de facturación del servicio de almacenamiento en la nube de terceros:
Descargo de responsabilidad: para obtener más información, consulte los términos relevantes en la licencia de código abierto AGPLv3 utilizado por SiYuan", "syncProvider": "Proveedor de servicios de almacenamiento en la nube", "syncProviderTip": "Si se selecciona, el servicio de almacenamiento en la nube de este proveedor se usará para lograr funciones de sincronización y copia de seguridad en la nube", diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 159c4c4c13c..7e5cc5a5e86 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "Système de fichiers local", + "deviceNotSupport": "L'appareil actuel n'est pas pris en charge", "second": "seconde", "syncInterval": "intervalle de synchronisation", "syncIntervalTip": "Synchronisation automatique des données après l'arrêt des modifications", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "Actuellement sélectionné est le service de stockage en nuage officiellement fourni par SiYuan, qui ne prend en charge que les utilisateurs d'abonnement payant", "syncThirdPartyProviderS3Intro": "L'actuel sélectionné est un service de stockage cloud tiers compatible avec le protocole Amazon S3", "syncThirdPartyProviderWebDAVIntro": "Le service de stockage cloud tiers du protocole WebDAV est actuellement sélectionné. En raison des performances médiocres et de la stabilité insuffisante de ce protocole, il est recommandé de choisir d'abord le service S3", + "syncThirdPartyProviderLocalIntro": "Le service de stockage fourni par le système de fichiers local est actuellement sélectionné. Veuillez saisir le chemin complet du dossier local dans le champ Endpoint", "syncThirdPartyProviderTip": "Avant d'utiliser cette fonctionnalité, veuillez prêter attention aux limitations techniques et aux éléments de facturation du service de stockage cloud tiers :
Avis de non-responsabilité : pour plus de détails, veuillez vous référer aux termes pertinents dans la licence open source AGPLv3 utilisé par SiYuan", "syncProvider": "Fournisseur de services de stockage en nuage", "syncProviderTip": "Si sélectionné, le service de stockage cloud de ce fournisseur sera utilisé pour réaliser les fonctions de synchronisation et de sauvegarde cloud", diff --git a/app/appearance/langs/he_IL.json b/app/appearance/langs/he_IL.json index f08a8234784..9d4c6e73248 100644 --- a/app/appearance/langs/he_IL.json +++ b/app/appearance/langs/he_IL.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "מערכת קבצים מקומית", + "deviceNotSupport": "המכשיר הנוכחי אינו נתמך", "second": "שנייה", "syncInterval": "מרווח סנכרון", "syncIntervalTip": "סנכרון נתונים אוטומטי לאחר שהנתונים מפסיקים להשתנות", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "נבחר עתה הוא שירות האחסון בענן המוצע רשמית על ידי SiYuan, אשר תומך רק במשתמשים בתשלום", "syncThirdPartyProviderS3Intro": "הנבחר עתה הוא שירות אחסון בענן צד שלישי תואם עם פרוטוקול Amazon S3 (למשל Qiniu, Aliyun OSS, Cloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "הנבחר עתה הוא שירות אחסון בענן צד שלישי עם פרוטוקול WebDAV. בשל ביצועים לקויים ויציבות בלתי מתאימה של פרוטוקול זה, הכרחי לבחור קודם בשירות S3", + "syncThirdPartyProviderLocalIntro": "שירות האחסון שסופק על ידי מערכת הקבצים המקומית נבחר כעת. אנא הזן את הנתיב המלא של התיקייה המקומית בשדה Endpoint", "syncThirdPartyProviderTip": "לפני השימוש בתכונה זו, אנא שים לב למגבלות טכניות ופריטי חיוב של שירות האחסון בענן של צד שלישי:
הבהרה: למידע נוסף, עיין בתנאים הרלוונטיים ברישיון הקוד הפתוח AGPLv3 בו משתמשת SiYuan", "syncProvider": "ספק שירות אחסון בענן", "syncProviderTip": "אם נבחר, שירות האחסון בענן של ספק זה ישמש כדי להשיג פונקציות סנכרון וגיבוי בענן", diff --git a/app/appearance/langs/it_IT.json b/app/appearance/langs/it_IT.json index 5f372f257e2..f04bf2d2370 100644 --- a/app/appearance/langs/it_IT.json +++ b/app/appearance/langs/it_IT.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "File system locale", + "deviceNotSupport": "Il dispositivo corrente non è supportato", "second": "secondo", "syncInterval": "intervallo di sincronizzazione", "syncIntervalTip": "Sincronizzazione automatica dei dati dopo che non ci sono più variazioni", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "Attualmente è selezionato il servizio di archiviazione cloud ufficiale fornito da SiYuan, che supporta solo utenti abbonati a pagamento", "syncThirdPartyProviderS3Intro": "Attualmente è selezionato un servizio di archiviazione cloud di terze parti compatibile con il protocollo Amazon S3 (es. Qiniu, Aliyun OSS, Cloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "Attualmente è selezionato un servizio di archiviazione cloud di terze parti con protocollo WebDAV. A causa delle scarse prestazioni e della stabilità insufficiente di questo protocollo, si consiglia di scegliere prima il servizio S3", + "syncThirdPartyProviderLocalIntro": "Attualmente è selezionato il servizio di archiviazione fornito dal file system locale. Inserisci il percorso completo della cartella locale nel campo Endpoint", "syncThirdPartyProviderTip": "Prima di utilizzare questa funzione, presta attenzione ai limiti tecnici e agli elementi di fatturazione del servizio di archiviazione cloud di terze parti:
Disclaimer: Per dettagli, si prega di consultare i termini pertinenti nella licenza open source AGPLv3 utilizzata da SiYuan", "syncProvider": "Provider di servizio di archiviazione cloud", "syncProviderTip": "Se selezionato, verrà utilizzato il servizio di archiviazione cloud di questo provider per sincronizzare e fare il backup dei dati", diff --git a/app/appearance/langs/ja_JP.json b/app/appearance/langs/ja_JP.json index 196ab3d44bf..0d12fb1a9bd 100644 --- a/app/appearance/langs/ja_JP.json +++ b/app/appearance/langs/ja_JP.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "ローカルファイルシステム", + "deviceNotSupport": "現在のデバイスはサポートされていません", "second": "秒", "syncInterval": "同期間隔", "syncIntervalTip": "データが変動しなくなった後に自動的にデータを同期します", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "現在選択されているのは SiYuan が公式に提供するクラウドストレージサービスで、有料サブスクリプションユーザーのみが利用可能です", "syncThirdPartyProviderS3Intro": "現在選択されているのは Amazon S3 プロトコルに準拠したサードパーティのクラウドストレージサービスです (例: QiniuAliyun OSSCloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "現在選択されているのは WebDAV プロトコルに対応したサードパーティのクラウドストレージサービスです。このプロトコルはパフォーマンスが低く安定性に欠けるため、S3 サービスを優先して選択することをお勧めします", + "syncThirdPartyProviderLocalIntro": "現在、ローカルファイルシステムが提供するストレージサービスが選択されています。Endpointフィールドにローカルフォルダの完全なパスを入力してください", "syncThirdPartyProviderTip": "この機能を使用する前にサードパーティのクラウドストレージサービスの技術的な制限と請求項目に注意してください:
免責事項: 詳細は SiYuan が使用するオープンソースライセンス AGPLv3 の関連規約を参照してください", "syncProvider": "クラウドストレージサービスプロバイダー", "syncProviderTip": "選択したプロバイダーのクラウドストレージサービスがクラウド同期とバックアップ機能を実現するために使用されます", diff --git a/app/appearance/langs/pl_PL.json b/app/appearance/langs/pl_PL.json index 1031d70f029..edddee0eb38 100644 --- a/app/appearance/langs/pl_PL.json +++ b/app/appearance/langs/pl_PL.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "Lokalny system plików", + "deviceNotSupport": "Bieżące urządzenie nie jest obsługiwane", "second": "sekunda", "syncInterval": "interwał synchronizacji", "syncIntervalTip": "Automatyczna synchronizacja danych po zaprzestaniu zmian", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "Aktualnie wybrano usługę pamięci w chmurze oficjalnie dostarczaną przez SiYuan, która obsługuje tylko płatnych subskrybentów", "syncThirdPartyProviderS3Intro": "Wybrano aktualnie jest usługą pamięci w chmurze firmy trzeciej zgodną z protokołem Amazon S3 (np. Qiniu, Aliyun OSS, Cloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "Wybrano obecnie jest usługą pamięci w chmurze firmy trzeciej z protokołem WebDAV. Z powodu słabej wydajności i niewystarczającej stabilności tego protokołu zaleca się najpierw wybranie usługi S3", + "syncThirdPartyProviderLocalIntro": "Obecnie wybrana jest usługa pamięci masowej dostarczana przez lokalny system plików. Wprowadź pełną ścieżkę do lokalnego folderu w polu Endpoint", "syncThirdPartyProviderTip": "Przed użyciem tej funkcji proszę zwrócić uwagę na ograniczenia techniczne i pozycje rozliczeniowe usługi pamięci w chmurze firmy trzeciej:
Zastrzeżenie: Aby uzyskać szczegóły, należy się odwołać do odpowiednich warunków w licencji open source AGPLv3 używanej przez SiYuan", "syncProvider": "Dostawca usługi pamięci w chmurze", "syncProviderTip": "Jeśli zostanie wybrany, usługa pamięci w chmurze tego dostawcy zostanie wykorzystana do realizacji funkcji synchronizacji i kopii zapasowej w chmurze", diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json index e7c3dc25687..c8519751518 100644 --- a/app/appearance/langs/ru_RU.json +++ b/app/appearance/langs/ru_RU.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "Локальная файловая система", + "deviceNotSupport": "Текущее устройство не поддерживается", "second": "секунда", "syncInterval": "интервал синхронизации", "syncIntervalTip": "Автоматическая синхронизация данных после прекращения изменений", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "В настоящее время выбрана облачная служба хранения, официально предоставляемая SiYuan, которая поддерживает только пользователей платной подписки", "syncThirdPartyProviderS3Intro": "В настоящее время выбрана служба облачного хранения третьей стороны, совместимая с протоколом Amazon S3 (например, Qiniu, Aliyun OSS, Cloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "В настоящее время выбрана облачная служба хранения, третьей стороны с протоколом WebDAV. Из-за низкой производительности и недостаточной стабильности этого протокола рекомендуется сначала выбрать службу S3", + "syncThirdPartyProviderLocalIntro": "В настоящее время выбрана служба хранения, предоставляемая локальной файловой системой. Введите полный путь к локальной папке в поле Endpoint", "syncThirdPartyProviderTip": "Перед использованием этой функции, пожалуйста, обратите внимание на технические ограничения и пункты выставления счетов службы облачного хранения третьей стороны:
Отказ от ответственности: Для получения дополнительных сведений смотрите соответствующие условия в лицензии открытого исходного кода AGPLv3, используемой SiYuan", "syncProvider": "Поставщик услуги облачного хранения", "syncProviderTip": "Если выбрано, будет использоваться облачное хранилище этого провайдера для реализации функций синхронизации и резервного копирования в облаке", diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index d973c0ec3ae..a93f5209a88 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "本地檔案系統", + "deviceNotSupport": "當前設備不支援", "second": "秒", "syncInterval": "同步間隔", "syncIntervalTip": "數據不再變動後自動進行數據同步", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "當前選擇的是 SiYuan 官方提供的雲端存儲服務,僅支持付費訂閱用戶使用", "syncThirdPartyProviderS3Intro": "當前選擇的是 Amazon S3 協議兼容的第三方雲端存儲服務(比如七牛雲阿里雲 OSSCloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "當前選擇的是 WebDAV 協議的第三方雲端存儲服務。由於該協議性能較差且穩定性不足,建議優先選擇 S3 服務", + "syncThirdPartyProviderLocalIntro": "目前選擇的是本地文件系統提供的儲存服務。請在 Endpoint 欄位中輸入本地資料夾的完整路徑", "syncThirdPartyProviderTip": "在使用該特性前,請注意第三方雲端存儲服務的技術限制和計費項目等條款:
免責聲明:詳見思源筆記所使用的開源許可證 AGPLv3 中的相關條款", "syncProvider": "雲端存儲服務提供商", "syncProviderTip": "選擇後將使用該提供商的雲端存儲服務實現雲端同步和備份功能", diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index 59e7df38c17..613fbffb6ba 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1,4 +1,6 @@ { + "localFlieSystem": "本地文件系统", + "deviceNotSupport": "当前设备不支持", "second": "秒", "syncInterval": "同步间隔", "syncIntervalTip": "数据不再变动后自动进行数据同步", @@ -475,6 +477,7 @@ "syncOfficialProviderIntro": "当前选择的是 SiYuan 官方提供的云端存储服务,仅支持付费订阅用户使用", "syncThirdPartyProviderS3Intro": "当前选择的是 Amazon S3 协议兼容的第三方云端存储服务(比如七牛云阿里云 OSSCloudflare R2)", "syncThirdPartyProviderWebDAVIntro": "当前选择的是 WebDAV 协议的第三方云端存储服务。由于该协议性能较差且稳定性不足,建议优先选择 S3 服务", + "syncThirdPartyProviderLocalIntro": "当前选择的是本地文件系统提供的存储服务。请在 Endpoint 字段中输入本地文件夹的完整路径", "syncThirdPartyProviderTip": "在使用该特性前,请注意第三方云端存储服务的技术限制和计费项目等条款:
免责声明:详见思源笔记所使用的开源许可证 AGPLv3 中的相关条款", "syncProvider": "云端存储服务提供商", "syncProviderTip": "选择后将使用该提供商的云端存储服务实现云端同步和备份功能", diff --git a/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20230805231614-vqn28eh/20230805231816-h1z9mpc/20230805232018-hgrq0ju.sy b/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20230805231614-vqn28eh/20230805231816-h1z9mpc/20230805232018-hgrq0ju.sy index 886f2da3c28..4c6cba5656c 100644 --- a/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20230805231614-vqn28eh/20230805231816-h1z9mpc/20230805232018-hgrq0ju.sy +++ b/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20230805231614-vqn28eh/20230805231816-h1z9mpc/20230805232018-hgrq0ju.sy @@ -6,7 +6,7 @@ "id": "20230805232018-hgrq0ju", "title": "Connect with third-party cloud storage", "type": "doc", - "updated": "20241216171745" + "updated": "20241231014303" }, "Children": [ { @@ -56,7 +56,7 @@ "ListData": {}, "Properties": { "id": "20230805232156-kbi85az", - "updated": "20241216171745" + "updated": "20241231014303" }, "Children": [ { @@ -229,6 +229,34 @@ ] } ] + }, + { + "ID": "20241231014251-chridot", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20241231014251-chridot", + "updated": "20241231014303" + }, + "Children": [ + { + "ID": "20241231014251-9owrhd5", + "Type": "NodeParagraph", + "Properties": { + "id": "20241231014251-9owrhd5", + "updated": "20241231014303" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "Local file system" + } + ] + } + ] } ] }, diff --git a/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20230805222417-2lj3dvk/20230805225107-qm1m2f5/20230805230131-sn7obzb.sy b/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20230805222417-2lj3dvk/20230805225107-qm1m2f5/20230805230131-sn7obzb.sy index 2e1c8a3e9b2..f428a0e9018 100644 --- a/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20230805222417-2lj3dvk/20230805225107-qm1m2f5/20230805230131-sn7obzb.sy +++ b/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20230805222417-2lj3dvk/20230805225107-qm1m2f5/20230805230131-sn7obzb.sy @@ -6,7 +6,7 @@ "id": "20230805230131-sn7obzb", "title": "对接第三方云端存储", "type": "doc", - "updated": "20241216171525" + "updated": "20241231014122" }, "Children": [ { @@ -56,7 +56,7 @@ "ListData": {}, "Properties": { "id": "20230805230746-4q9cmrb", - "updated": "20241216171525" + "updated": "20241231014122" }, "Children": [ { @@ -219,6 +219,34 @@ ] } ] + }, + { + "ID": "20241231014119-rkud23t", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20241231014119-rkud23t", + "updated": "20241231014122" + }, + "Children": [ + { + "ID": "20241231014119-fdfwv7d", + "Type": "NodeParagraph", + "Properties": { + "id": "20241231014119-fdfwv7d", + "updated": "20241231014122" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "本地文件系统" + } + ] + } + ] } ] }, diff --git a/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20230805232636-zh0adz2/20230805232719-04mqbcx/20230805232903-erdoerp.sy b/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20230805232636-zh0adz2/20230805232719-04mqbcx/20230805232903-erdoerp.sy index 4edbbbfd0c5..dd6ec58c4a0 100644 --- a/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20230805232636-zh0adz2/20230805232719-04mqbcx/20230805232903-erdoerp.sy +++ b/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20230805232636-zh0adz2/20230805232719-04mqbcx/20230805232903-erdoerp.sy @@ -6,7 +6,7 @@ "id": "20230805232903-erdoerp", "title": "對接第三方雲端存儲", "type": "doc", - "updated": "20241216171627" + "updated": "20241231014235" }, "Children": [ { @@ -56,7 +56,7 @@ "ListData": {}, "Properties": { "id": "20230805232945-ugjx8sl", - "updated": "20241216171627" + "updated": "20241231014235" }, "Children": [ { @@ -229,6 +229,34 @@ ] } ] + }, + { + "ID": "20241231014229-xkbbezi", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20241231014229-xkbbezi", + "updated": "20241231014235" + }, + "Children": [ + { + "ID": "20241231014229-5oi1vuc", + "Type": "NodeParagraph", + "Properties": { + "id": "20241231014229-5oi1vuc", + "updated": "20241231014235" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "本地檔案系統" + } + ] + } + ] } ] }, diff --git a/app/guide/20240530133126-axarxgx/20240530101000-4qitucx/20240530101000-cb37szr/20240530101000-qf0xtkd/20240530101000-na9sys7.sy b/app/guide/20240530133126-axarxgx/20240530101000-4qitucx/20240530101000-cb37szr/20240530101000-qf0xtkd/20240530101000-na9sys7.sy index 848970ea2bd..438274a7041 100644 --- a/app/guide/20240530133126-axarxgx/20240530101000-4qitucx/20240530101000-cb37szr/20240530101000-qf0xtkd/20240530101000-na9sys7.sy +++ b/app/guide/20240530133126-axarxgx/20240530101000-4qitucx/20240530101000-cb37szr/20240530101000-qf0xtkd/20240530101000-na9sys7.sy @@ -7,7 +7,7 @@ "id": "20240530101000-na9sys7", "title": "サードパーティのクラウドストレージへの接続", "type": "doc", - "updated": "20241216171841" + "updated": "20241231014328" }, "Children": [ { @@ -58,7 +58,7 @@ "ListData": {}, "Properties": { "id": "20240530101000-y5kmw6j", - "updated": "20241216171841" + "updated": "20241231014328" }, "Children": [ { @@ -244,6 +244,34 @@ ] } ] + }, + { + "ID": "20241231014316-b0vcf9w", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20241231014316-b0vcf9w", + "updated": "20241231014328" + }, + "Children": [ + { + "ID": "20241231014316-5w9cag8", + "Type": "NodeParagraph", + "Properties": { + "id": "20241231014316-5w9cag8", + "updated": "20241231014328" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "ローカルファイルシステム" + } + ] + } + ] } ] }, diff --git a/app/src/config/repos.ts b/app/src/config/repos.ts index f857a3893af..712e9b680e5 100644 --- a/app/src/config/repos.ts +++ b/app/src/config/repos.ts @@ -6,6 +6,7 @@ import {processSync} from "../dialog/processSystem"; import {getCloudURL} from "./util/about"; import {openByMobile} from "../protyle/util/compatibility"; import {confirmDialog} from "../dialog/confirmDialog"; +import {isKernelInMobile} from "../util/functions"; const renderProvider = (provider: number) => { if (provider === 0) { @@ -124,7 +125,7 @@ const renderProvider = (provider: number) => { ${window.siyuan.languages.syncThirdPartyProviderWebDAVIntro}
${window.siyuan.languages.proFeature} -
+
${window.siyuan.languages.syncThirdPartyProviderTip}
@@ -177,6 +178,36 @@ const renderProvider = (provider: number) => { +
`; + } else if (provider === 4) { + if (isKernelInMobile()) { + return `
+ ${window.siyuan.languages.syncThirdPartyProviderLocalIntro} +
+ ${window.siyuan.languages.proFeature} +
+ ${window.siyuan.languages.deviceNotSupport} +
`; + } + return `
+ ${window.siyuan.languages.syncThirdPartyProviderLocalIntro} +
+ ${window.siyuan.languages.proFeature} +
+
+
Endpoint
+
+ +
+
+
Timeout (s)
+
+ +
+
+
Concurrent Reqs
+
+
`; } return ""; @@ -326,6 +357,39 @@ const bindProviderEvent = () => { fetchPost("/api/sync/setSyncProviderWebDAV", {webdav}, () => { window.siyuan.config.sync.webdav = webdav; }); + } else if (window.siyuan.config.sync.provider === 4) { + let timeout = parseInt((providerPanelElement.querySelector("#timeout") as HTMLInputElement).value, 10); + if (7 > timeout) { + timeout = 7; + } + if (300 < timeout) { + timeout = 300; + } + let concurrentReqs = parseInt((providerPanelElement.querySelector("#localConcurrentReqs") as HTMLInputElement).value, 10); + if (1 > concurrentReqs) { + concurrentReqs = 1; + } + if (1024 < concurrentReqs) { + concurrentReqs = 1024; + } + (providerPanelElement.querySelector("#timeout") as HTMLInputElement).value = timeout.toString(); + const local = { + endpoint: (providerPanelElement.querySelector("#endpoint") as HTMLInputElement).value, + timeout: timeout, + concurrentReqs: concurrentReqs, + }; + fetchPost("/api/sync/setSyncProviderLocal", {local}, (response) => { + if (response.code === 0) { + window.siyuan.config.sync.local = response.data.local; + + const endpoint = providerPanelElement.querySelector("#endpoint"); + if (endpoint) { + endpoint.value = response.data.local.endpoint; + } + } else { + window.siyuan.config.sync.local = local; + } + }); } }); }); @@ -348,6 +412,7 @@ export const repos = { +
diff --git a/app/src/types/config.d.ts b/app/src/types/config.d.ts index 22750f37df7..11aafbf8610 100644 --- a/app/src/types/config.d.ts +++ b/app/src/types/config.d.ts @@ -1376,6 +1376,7 @@ declare namespace Config { */ synced: number; webdav: ISyncWebDAV; + local: ISyncLocal; } /** @@ -1450,6 +1451,28 @@ declare namespace Config { username: string; } + /** + * Local file system related configuration + */ + export interface ISyncLocal { + /** + * The full path of local directory + * + * Examples: + * - Windows: `"D:/path/to/repos/directory"` + * - Unix: `"/path/to/repos/directory"` + */ + endpoint: string; + /** + * Timeout (unit: seconds) + */ + timeout: number; + /** + * Concurrent requests. + */ + concurrentReqs: number; + } + /** * System related information */ @@ -1474,6 +1497,7 @@ declare namespace Config { * - `docker`: Docker container * - `android`: Android device * - `ios`: iOS device + * - `harmony`: HarmonyOS device * - `std`: Desktop Electron environment */ container: TSystemContainer; diff --git a/app/src/util/functions.ts b/app/src/util/functions.ts index 843e28ba951..69625368cf4 100644 --- a/app/src/util/functions.ts +++ b/app/src/util/functions.ts @@ -1,16 +1,28 @@ +const CONTAINER_BACKEND_SET = new Set(["docker", "ios", "android", "harmony"]); +const MOBILE_BACKEND_SET = new Set(["ios", "android", "harmony"]); + +export const isKernelInContainer = (): boolean => { + return CONTAINER_BACKEND_SET.has(window.siyuan.config.system.container); +}; + +export const isKernelInMobile = (): boolean => { + return MOBILE_BACKEND_SET.has(window.siyuan.config.system.container); +}; + export const isMobile = () => { return document.getElementById("sidebar") ? true : false; }; // "windows" | "linux" | "darwin" | "docker" | "android" | "ios" | "harmony" export const getBackend = () => { - if (["docker", "ios", "android", "harmony"].includes(window.siyuan.config.system.container)) { + if (isKernelInContainer()) { return window.siyuan.config.system.container; } else { return window.siyuan.config.system.os; } }; + // "desktop" | "desktop-window" | "mobile" | "browser-desktop" | "browser-mobile" export const getFrontend = () => { /// #if MOBILE diff --git a/kernel/api/router.go b/kernel/api/router.go index c2f4c0d9b1e..ef65d32cc84 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -245,6 +245,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/sync/setSyncProvider", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncProvider) ginServer.Handle("POST", "/api/sync/setSyncProviderS3", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncProviderS3) ginServer.Handle("POST", "/api/sync/setSyncProviderWebDAV", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncProviderWebDAV) + ginServer.Handle("POST", "/api/sync/setSyncProviderLocal", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setSyncProviderLocal) ginServer.Handle("POST", "/api/sync/setCloudSyncDir", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setCloudSyncDir) ginServer.Handle("POST", "/api/sync/createCloudSyncDir", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, createCloudSyncDir) ginServer.Handle("POST", "/api/sync/removeCloudSyncDir", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeCloudSyncDir) diff --git a/kernel/api/sync.go b/kernel/api/sync.go index 6276dfb1a42..a2053425bc9 100644 --- a/kernel/api/sync.go +++ b/kernel/api/sync.go @@ -667,6 +667,45 @@ func setSyncProviderWebDAV(c *gin.Context) { } } +func setSyncProviderLocal(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + localArg := arg["local"].(interface{}) + data, err := gulu.JSON.MarshalJSON(localArg) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 5000} + return + } + + local := &conf.Local{} + if err = gulu.JSON.UnmarshalJSON(data, local); err != nil { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 5000} + return + } + + err = model.SetSyncProviderLocal(local) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 5000} + return + } + + ret.Data = map[string]interface{}{ + "local": local, + } +} + func setCloudSyncDir(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/conf/sync.go b/kernel/conf/sync.go index 488cb9e5701..1f23e9642ca 100644 --- a/kernel/conf/sync.go +++ b/kernel/conf/sync.go @@ -28,6 +28,7 @@ type Sync struct { Provider int `json:"provider"` // 云端存储服务提供者 S3 *S3 `json:"s3"` // S3 对象存储服务配置 WebDAV *WebDAV `json:"webdav"` // WebDAV 服务配置 + Local *Local `json:"local"` // 本地文件系统 服务配置 } func NewSync() *Sync { @@ -63,10 +64,17 @@ type WebDAV struct { ConcurrentReqs int `json:"concurrentReqs"` // 并发请求数 } +type Local struct { + Endpoint string `json:"endpoint"` // 服务端点 (本地文件系统目录) + Timeout int `json:"timeout"` // 超时时间,单位:秒 + ConcurrentReqs int `json:"concurrentReqs"` // 并发请求数 +} + const ( ProviderSiYuan = 0 // ProviderSiYuan 为思源官方提供的云端存储服务 ProviderS3 = 2 // ProviderS3 为 S3 协议对象存储提供的云端存储服务 ProviderWebDAV = 3 // ProviderWebDAV 为 WebDAV 协议提供的云端存储服务 + ProviderLocal = 4 // ProviderLocal 为本地文件系统提供的存储服务 ) func ProviderToStr(provider int) string { @@ -77,6 +85,8 @@ func ProviderToStr(provider int) string { return "S3" case ProviderWebDAV: return "WebDAV" + case ProviderLocal: + return "Local File System" } return "Unknown" } diff --git a/kernel/model/conf.go b/kernel/model/conf.go index 1b2eae6691a..d80d043fc1d 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -360,6 +360,13 @@ func InitConf() { Conf.Sync.WebDAV.Endpoint = util.NormalizeEndpoint(Conf.Sync.WebDAV.Endpoint) Conf.Sync.WebDAV.Timeout = util.NormalizeTimeout(Conf.Sync.WebDAV.Timeout) Conf.Sync.WebDAV.ConcurrentReqs = util.NormalizeConcurrentReqs(Conf.Sync.WebDAV.ConcurrentReqs, conf.ProviderWebDAV) + if nil == Conf.Sync.Local { + Conf.Sync.Local = &conf.Local{} + } + Conf.Sync.Local.Endpoint = util.NormalizeLocalPath(Conf.Sync.Local.Endpoint) + Conf.Sync.Local.Timeout = util.NormalizeTimeout(Conf.Sync.Local.Timeout) + Conf.Sync.Local.ConcurrentReqs = util.NormalizeConcurrentReqs(Conf.Sync.Local.ConcurrentReqs, conf.ProviderLocal) + if util.ContainerDocker == util.Container { Conf.Sync.Perception = false } diff --git a/kernel/model/repository.go b/kernel/model/repository.go index 359bfc97fb6..47c9dac7074 100644 --- a/kernel/model/repository.go +++ b/kernel/model/repository.go @@ -803,7 +803,7 @@ func DownloadCloudSnapshot(tag, id string) (err error) { util.PushErrMsg(Conf.Language(29), 5000) return } - case conf.ProviderWebDAV, conf.ProviderS3: + case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { util.PushErrMsg(Conf.Language(214), 5000) return @@ -845,7 +845,7 @@ func UploadCloudSnapshot(tag, id string) (err error) { util.PushErrMsg(Conf.Language(29), 5000) return } - case conf.ProviderWebDAV, conf.ProviderS3: + case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { util.PushErrMsg(Conf.Language(214), 5000) return @@ -891,7 +891,7 @@ func RemoveCloudRepoTag(tag string) (err error) { util.PushErrMsg(Conf.Language(29), 5000) return } - case conf.ProviderWebDAV, conf.ProviderS3: + case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { util.PushErrMsg(Conf.Language(214), 5000) return @@ -923,7 +923,7 @@ func GetCloudRepoTagSnapshots() (ret []*dejavu.Log, err error) { util.PushErrMsg(Conf.Language(29), 5000) return } - case conf.ProviderWebDAV, conf.ProviderS3: + case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { util.PushErrMsg(Conf.Language(214), 5000) return @@ -959,7 +959,7 @@ func GetCloudRepoSnapshots(page int) (ret []*dejavu.Log, pageCount, totalCount i util.PushErrMsg(Conf.Language(29), 5000) return } - case conf.ProviderWebDAV, conf.ProviderS3: + case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { util.PushErrMsg(Conf.Language(214), 5000) return @@ -1845,6 +1845,8 @@ func newRepository() (ret *dejavu.Repo, err error) { webdavClient.SetTimeout(time.Duration(cloudConf.WebDAV.Timeout) * time.Second) webdavClient.SetTransport(httpclient.NewTransport(cloudConf.WebDAV.SkipTlsVerify)) cloudRepo = cloud.NewWebDAV(&cloud.BaseCloud{Conf: cloudConf}, webdavClient) + case conf.ProviderLocal: + cloudRepo = cloud.NewLocal(&cloud.BaseCloud{Conf: cloudConf}) default: err = fmt.Errorf("unknown cloud provider [%d]", Conf.Sync.Provider) return @@ -2129,6 +2131,12 @@ func buildCloudConf() (ret *cloud.Conf, err error) { Timeout: Conf.Sync.WebDAV.Timeout, ConcurrentReqs: Conf.Sync.WebDAV.ConcurrentReqs, } + case conf.ProviderLocal: + ret.Local = &cloud.ConfLocal{ + Endpoint: Conf.Sync.Local.Endpoint, + Timeout: Conf.Sync.Local.Timeout, + ConcurrentReqs: Conf.Sync.Local.ConcurrentReqs, + } default: err = fmt.Errorf("invalid provider [%d]", Conf.Sync.Provider) return diff --git a/kernel/model/sync.go b/kernel/model/sync.go index 3a104342eec..9f37a0fe23b 100644 --- a/kernel/model/sync.go +++ b/kernel/model/sync.go @@ -252,7 +252,7 @@ func checkSync(boot, exit, byHand bool) bool { if !IsSubscriber() { return false } - case conf.ProviderWebDAV, conf.ProviderS3: + case conf.ProviderWebDAV, conf.ProviderS3, conf.ProviderLocal: if !IsPaidUser() { return false } @@ -471,13 +471,28 @@ func SetSyncProviderWebDAV(webdav *conf.WebDAV) (err error) { return } +func SetSyncProviderLocal(local *conf.Local) (err error) { + local.Endpoint = strings.TrimSpace(local.Endpoint) + local.Endpoint = util.NormalizeLocalPath(local.Endpoint) + + local.Timeout = util.NormalizeTimeout(local.Timeout) + local.ConcurrentReqs = util.NormalizeConcurrentReqs(local.ConcurrentReqs, conf.ProviderLocal) + + Conf.Sync.Local = local + Conf.Save() + return +} + var ( syncLock = sync.Mutex{} isSyncing = atomic.Bool{} ) func CreateCloudSyncDir(name string) (err error) { - if conf.ProviderSiYuan != Conf.Sync.Provider { + switch Conf.Sync.Provider { + case conf.ProviderSiYuan, conf.ProviderLocal: + break + default: err = errors.New(Conf.Language(131)) return } @@ -502,7 +517,10 @@ func CreateCloudSyncDir(name string) (err error) { } func RemoveCloudSyncDir(name string) (err error) { - if conf.ProviderSiYuan != Conf.Sync.Provider { + switch Conf.Sync.Provider { + case conf.ProviderSiYuan, conf.ProviderLocal: + break + default: err = errors.New(Conf.Language(131)) return } @@ -678,6 +696,9 @@ func isProviderOnline(byHand bool) (ret bool) { checkURL = Conf.Sync.WebDAV.Endpoint skipTlsVerify = Conf.Sync.WebDAV.SkipTlsVerify timeout = Conf.Sync.WebDAV.Timeout * 1000 + case conf.ProviderLocal: + checkURL = "file://" + Conf.Sync.Local.Endpoint + timeout = Conf.Sync.Local.Timeout * 1000 default: logging.LogWarnf("unknown provider: %d", Conf.Sync.Provider) return false diff --git a/kernel/util/net.go b/kernel/util/net.go index 60b8a2fe9d0..1b49a83df35 100644 --- a/kernel/util/net.go +++ b/kernel/util/net.go @@ -20,6 +20,7 @@ import ( "net" "net/http" "net/url" + "os" "strconv" "strings" "time" @@ -73,15 +74,20 @@ func IsLocalOrigin(origin string) bool { } func IsOnline(checkURL string, skipTlsVerify bool, timeout int) bool { - _, err := url.Parse(checkURL) - if err != nil { - logging.LogWarnf("invalid check URL [%s]", checkURL) + if "" == checkURL { return false } - if "" == checkURL { + u, err := url.Parse(checkURL) + if err != nil { + logging.LogWarnf("invalid check URL [%s]", checkURL) return false } + if u.Scheme == "file" { + filePath := strings.TrimPrefix(checkURL, "file://") + _, err := os.Stat(filePath) + return err == nil + } if isOnline(checkURL, skipTlsVerify, timeout) { return true diff --git a/kernel/util/path.go b/kernel/util/path.go index ed562a514d5..b9f4f9f7b47 100644 --- a/kernel/util/path.go +++ b/kernel/util/path.go @@ -188,16 +188,39 @@ func GetChildDocDepth(treeAbsPath string) (ret int) { } func NormalizeConcurrentReqs(concurrentReqs int, provider int) int { - if 1 > concurrentReqs { - if 2 == provider { // S3 - return 8 - } else if 3 == provider { // WebDAV - return 1 + switch provider { + case 0: // SiYuan + switch { + case concurrentReqs < 1: + concurrentReqs = 8 + case concurrentReqs > 16: + concurrentReqs = 16 + default: + } + case 2: // S3 + switch { + case concurrentReqs < 1: + concurrentReqs = 8 + case concurrentReqs > 16: + concurrentReqs = 16 + default: + } + case 3: // WebDAV + switch { + case concurrentReqs < 1: + concurrentReqs = 1 + case concurrentReqs > 16: + concurrentReqs = 16 + default: + } + case 4: // Local File System + switch { + case concurrentReqs < 1: + concurrentReqs = 16 + case concurrentReqs > 1024: + concurrentReqs = 1024 + default: } - return 8 - } - if 16 < concurrentReqs { - return 16 } return concurrentReqs } @@ -229,6 +252,18 @@ func NormalizeEndpoint(endpoint string) string { return endpoint } +func NormalizeLocalPath(endpoint string) string { + endpoint = strings.TrimSpace(endpoint) + if "" == endpoint { + return "" + } + endpoint = filepath.ToSlash(filepath.Clean(endpoint)) + if !strings.HasSuffix(endpoint, "/") { + endpoint = endpoint + "/" + } + return endpoint +} + func FilterMoveDocFromPaths(fromPaths []string, toPath string) (ret []string) { tmp := FilterSelfChildDocs(fromPaths) for _, fromPath := range tmp {