mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-02-08 00:04:21 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
ea2f711937
30 changed files with 981 additions and 59 deletions
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "اتبع شاشة قفل النظام",
|
||||
"about8": "بعد التمكين، سيتم قفل التطبيق تلقائياً عند قفل شاشة النظام",
|
||||
"about11": "خادم الشبكة",
|
||||
"about12": "بعد التمكين، سيتم السماح للأجهزة الأخرى في نفس الشبكة المحلية بالوصول. سيتم إغلاق التطبيق تلقائياً بعد التعديل، يرجى إعادة التشغيل يدوياً",
|
||||
"about12": "عند التمكين، سيسمح لأجهزة أخرى على نفس الشبكة المحلية بالوصول. بعد التعديل سيتم إغلاق التطبيق تلقائيًا؛ يرجى إعادة التشغيل يدويًا",
|
||||
"networkServeTLS": "تمكين HTTPS",
|
||||
"networkServeTLSTip": "عند التمكين ستستخدم الاتصالات الشبكية شهادة موقعة ذاتيًا تم إنشاؤها تلقائيًا لتشفير TLS. سوف يعرض المتصفح تحذير أمني يجب قبوله يدويًا. بعد التعديل سيتم إغلاق التطبيق تلقائيًا؛ يرجى إعادة التشغيل يدويًا",
|
||||
"networkServeTLSTip2": "HTTPS يعمل فقط على المنفذ الثابت 6806، المنافذ العشوائية لا تدعم HTTPS",
|
||||
"exportCACert": "تصدير شهادة CA",
|
||||
"exportCACertTip": "تصدير شهادة CA(ca.crt). قم بتثبيت هذه الشهادة على أجهزة العميل للثقة في اتصال HTTPS الموقع ذاتيًا",
|
||||
"exportCABundle": "تصدير حزمة CA",
|
||||
"exportCABundleTip": "تصدير شهادة CA والمفتاح الخاص لمشاركتها مع أجهزة SiYuan الأخرى. ستثق الأجهزة التي تستخدم نفس CA في الشهادات الصادرة عنها",
|
||||
"importCABundle": "استيراد حزمة CA",
|
||||
"importCABundleTip": "استيراد حزمة CA من جهاز SiYuan آخر. بعد الاستيراد سيستخدم هذا الجهاز نفس CA، مما يسمح للعملاء بالثقة في شهادات جميع الأجهزة",
|
||||
"importCABundleSuccess": "تم استيراد حزمة CA بنجاح,يرجى إعادة تشغيل التطبيق لتطبيق التغييرات",
|
||||
"about13": "رمز API",
|
||||
"about14": "\u202B مصادفة الرمز المميز عند استدعاء واجهة برمجة التطبيقات (API)\u202C<br>\u202Bرأس طلب HTTP هو \u202A<code class='fn__code'>Authorization: token ${token}</code>\u202C\u202C",
|
||||
"about17": "عند الضبط على <code class='fn__code'>اتصال مباشر</code> لن يتم تفعيل الوكيل",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Entspricht dem Systembildschirm",
|
||||
"about8": "Nach der Aktivierung wird die Anwendung automatisch gesperrt, wenn der Systembildschirm gesperrt wird",
|
||||
"about11": "Netzwerkservice",
|
||||
"about12": "Nach der Aktivierung dürfen andere Geräte im selben LAN zugreifen. Die Anwendung wird nach der Modifikation automatisch geschlossen, bitte manuell neu starten",
|
||||
"about12": "Wenn aktiviert, wird anderen Geräten im selben lokalen Netzwerk der Zugriff erlaubt. Nach der Änderung wird die Anwendung automatisch geschlossen, bitte manuell neu starten",
|
||||
"networkServeTLS": "HTTPS aktivieren",
|
||||
"networkServeTLSTip": "Wenn aktiviert, verwenden Netzwerkverbindungen ein automatisch generiertes selbstsigniertes Zertifikat für die TLS-Verschlüsselung. Der Browser zeigt eine Sicherheitswarnung an, die manuell akzeptiert werden muss. Nach der Änderung wird die Anwendung automatisch geschlossen, bitte manuell neu starten",
|
||||
"networkServeTLSTip2": "HTTPS funktioniert nur am festen Port 6806, zufällige Ports unterstützen HTTPS nicht",
|
||||
"exportCACert": "CA‑Zertifikat exportieren",
|
||||
"exportCACertTip": "Das CA‑Zertifikat(ca.crt)exportieren. Installieren Sie dieses Zertifikat auf Client‑Geräten, um der selbstsignierten HTTPS‑Verbindung zu vertrauen",
|
||||
"exportCABundle": "CA‑Bundle exportieren",
|
||||
"exportCABundleTip": "Exportieren Sie das CA‑Zertifikat und den privaten Schlüssel, um sie mit anderen SiYuan‑Geräten zu teilen. Geräte, die dieselbe CA verwenden, vertrauen den von ihr ausgestellten Zertifikaten",
|
||||
"importCABundle": "CA‑Bundle importieren",
|
||||
"importCABundleTip": "Importieren Sie das CA‑Bundle von einem anderen SiYuan‑Gerät. Nach dem Import verwendet dieses Gerät dieselbe CA, wodurch Clients Zertifikaten aller Geräte vertrauen können",
|
||||
"importCABundleSuccess": "CA‑Bundle erfolgreich importiert, bitte starten Sie die Anwendung neu, um die Änderungen anzuwenden",
|
||||
"about13": "API-Token",
|
||||
"about14": "Der Token muss bei der API-Anrufung authentifiziert werden<br>HTTP-Anforderungsheader <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "Proxy nicht aktivieren, wenn auf <code class='fn__code'>Direkte Verbindung</code> gesetzt",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Follow system lock screen",
|
||||
"about8": "When enabled, the application will be automatically locked when locking the system screen",
|
||||
"about11": "Network serving",
|
||||
"about12": "When enabled, other devices in the same LAN will be allowed to access. The application will be closed automatically after modification, please restart manually",
|
||||
"about12": "When enabled, allows other devices on the same local network to access. After modification the application will automatically close, please restart manually",
|
||||
"networkServeTLS": "Enable HTTPS",
|
||||
"networkServeTLSTip": "When enabled, network connections will use an automatically generated self-signed certificate for TLS encryption. The browser will show a security warning that must be accepted manually. After modification the application will automatically close, please restart manually",
|
||||
"networkServeTLSTip2": "HTTPS only works on the fixed port 6806, random ports do not support HTTPS",
|
||||
"exportCACert": "Export CA certificate",
|
||||
"exportCACertTip": "Export the CA certificate(ca.crt)file. Install this certificate on client devices to trust the self‑signed HTTPS connection",
|
||||
"exportCABundle": "Export CA bundle",
|
||||
"exportCABundleTip": "Export the CA certificate and private key for sharing with other SiYuan devices. Devices using the same CA will trust certificates issued by it",
|
||||
"importCABundle": "Import CA bundle",
|
||||
"importCABundleTip": "Import the CA bundle from another SiYuan device. After import this device will use the same CA, allowing clients to trust certificates from all such devices",
|
||||
"importCABundleSuccess": "CA bundle imported successfully, please restart the app to apply changes",
|
||||
"about13": "API token",
|
||||
"about14": "The token needs to be authenticated when calling the API<br>HTTP request header <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "Do not enable proxy when set to <code class='fn__code'>Direct connection</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Seguir la pantalla de bloqueo del sistema",
|
||||
"about8": "Después de habilitarlo, la aplicación se bloqueará automáticamente al bloquear la pantalla del sistema",
|
||||
"about11": "Red de servicio",
|
||||
"about12": "Después de habilitarlo, se permitirá el acceso a otros dispositivos de la misma LAN. La aplicación se cerrará automáticamente después de la modificación, por favor reinicie manualmente",
|
||||
"about12": "Al activarlo permitirá que otros dispositivos en la misma red local accedan. Después de la modificación la aplicación se cerrará automáticamente, por favor reinicie manualmente",
|
||||
"networkServeTLS": "Habilitar HTTPS",
|
||||
"networkServeTLSTip": "Al activarlo, las conexiones de red usarán un certificado autofirmado generado automáticamente para el cifrado TLS. El navegador mostrará una advertencia de seguridad que deberá aceptarse manualmente. Después de la modificación la aplicación se cerrará automáticamente, por favor reinicie manualmente",
|
||||
"networkServeTLSTip2": "HTTPS solo funciona en el puerto fijo 6806, los puertos aleatorios no admiten HTTPS",
|
||||
"exportCACert": "Exportar certificado CA",
|
||||
"exportCACertTip": "Exportar el certificado CA(ca.crt). Instale este certificado en los dispositivos cliente para confiar en la conexión HTTPS autofirmada",
|
||||
"exportCABundle": "Exportar paquete CA",
|
||||
"exportCABundleTip": "Exportar el certificado CA y la clave privada para compartirlos con otros dispositivos SiYuan. Los dispositivos que usen el mismo CA confiarán en los certificados emitidos por él",
|
||||
"importCABundle": "Importar paquete CA",
|
||||
"importCABundleTip": "Importar el paquete CA desde otro dispositivo SiYuan. Tras importarlo, este dispositivo usará el mismo CA, permitiendo que los clientes confíen en los certificados de todos los dispositivos",
|
||||
"importCABundleSuccess": "Paquete CA importado con éxito, reinicie la aplicación para aplicar los cambios",
|
||||
"about13": "Token API",
|
||||
"about14": "El token debe ser autentificado al llamar a la API<br>Encabezado de solicitud HTTP <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "No habilite el proxy cuando esté configurado como <code class='fn__code'>Conexión directa</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Suivez l'écran de verrouillage du système",
|
||||
"about8": "Après activation, l'application sera automatiquement verrouillée lors du verrouillage de l'écran système",
|
||||
"about11": "Service de réseau",
|
||||
"about12": "Après l'activation, les autres appareils du même réseau local seront autorisés à accéder. L'application sera fermée automatiquement après modification, veuillez la redémarrer manuellement.",
|
||||
"about12": "Une fois activé, permettra aux autres appareils du même réseau local d'accéder. Après la modification, l'application se fermera automatiquement, veuillez redémarrer manuellement",
|
||||
"networkServeTLS": "Activer HTTPS",
|
||||
"networkServeTLSTip": "Une fois activé, les connexions réseau utiliseront un certificat auto-signé généré automatiquement pour le chiffrement TLS. Le navigateur affichera un avertissement de sécurité qu'il faudra accepter manuellement. Après la modification, l'application se fermera automatiquement, veuillez redémarrer manuellement",
|
||||
"networkServeTLSTip2": "HTTPS est actif uniquement sur le port fixe 6806, les ports aléatoires ne prennent pas en charge HTTPS",
|
||||
"exportCACert": "Exporter le certificat CA",
|
||||
"exportCACertTip": "Exporter le certificat CA(ca.crt). Installez ce certificat sur les appareils clients pour faire confiance à la connexion HTTPS auto-signée",
|
||||
"exportCABundle": "Exporter le bundle CA",
|
||||
"exportCABundleTip": "Exporter le certificat CA et la clé privée pour les partager avec d'autres appareils SiYuan. Les appareils utilisant la même CA feront confiance aux certificats émis par celle-ci",
|
||||
"importCABundle": "Importer le bundle CA",
|
||||
"importCABundleTip": "Importer le bundle CA depuis un autre appareil SiYuan. Après importation cet appareil utilisera la même CA, permettant aux clients de faire confiance aux certificats de tous les appareils",
|
||||
"importCABundleSuccess": "Bundle CA importé avec succès, veuillez redémarrer l'application pour appliquer les modifications",
|
||||
"about13": "API token",
|
||||
"about14": "Le token doit être authentifié lors de l'appel de l'API<br>En-tête de requête HTTP <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "N'activez pas le proxy lorsqu'il est défini sur <code class='fn__code'>Connexion directe</code>.",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "עקוב אחרי מערכת נעילת המסך",
|
||||
"about8": "לאחר הפעלת אפשרות זו, האפליקציה תינעל באופן אוטומטי כאשר המסך של המערכת נעול",
|
||||
"about11": "שירות רשת",
|
||||
"about12": "לאחר הפעלת אפשרות זו, מכשירים אחרים באותה LAN יתמכו בגישה. האפליקציה תסגר אוטומטית לאחר שינוי, אנא הפעל אותה מחדש ידנית",
|
||||
"about12": "בהפעלה תתאפשר גישה למכשירים אחרים ברשת המקומית. לאחר השינוי האפליקציה תיסגר אוטומטית — יש להפעיל ידנית מחדש",
|
||||
"networkServeTLS": "הפעל HTTPS",
|
||||
"networkServeTLSTip": "בהפעלה חיבורי הרשת ישתמשו בתעודה חתומה עצמית שנוצרה אוטומטית להצפנת TLS. הדפדפן יציג אזהרת אבטחה שיש לקבל ידנית. לאחר השינוי האפליקציה תיסגר אוטומטית — יש להפעיל ידנית מחדש",
|
||||
"networkServeTLSTip2": "HTTPS פועל רק ביציאה הקבועה 6806, יציאות אקראיות אינן תומכות ב־HTTPS",
|
||||
"exportCACert": "ייצא תעודת CA",
|
||||
"exportCACertTip": "ייצא את תעודת ה‑CA(ca.crt). התקן תעודה זו במכשירי הלקוח כדי לתת אמון בחיבור HTTPS חתום עצמית",
|
||||
"exportCABundle": "ייצא חבילת CA",
|
||||
"exportCABundleTip": "ייצא את תעודת ה‑CA והמפתח הפרטי לשיתוף עם מכשירי SiYuan אחרים. מכשירים המשתמשים באותו CA יסתמכו על תעודות שהונפקו על ידו",
|
||||
"importCABundle": "ייבא חבילת CA",
|
||||
"importCABundleTip": "ייבא חבילת CA ממכשיר SiYuan אחר. לאחר הייבוא מכשיר זה ישתמש באותו CA, מה שיאפשר ללקוחות להאמין בתעודות מכל המכשירים",
|
||||
"importCABundleSuccess": "חבילת CA יובאה בהצלחה, אנא הפעל מחדש את האפליקציה כדי להחיל את השינויים",
|
||||
"about13": "אסימון API",
|
||||
"about14": "האשרה צריכה להיות מאומתת כאשר מתקשרים ל-API<br>כותרת הבקשה HTTP <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "אל תאפשר פרוקסי כאשר זה מוגדר ל<code class='fn__code'>חיבור ישיר</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Segui il blocco schermo del sistema",
|
||||
"about8": "Dopo l'abilitazione, l'applicazione verrà automaticamente bloccata quando blocchi lo schermo del sistema",
|
||||
"about11": "Servizio di rete",
|
||||
"about12": "Dopo l'abilitazione, altri dispositivi nella stessa LAN potranno accedere. L'applicazione verrà chiusa automaticamente dopo la modifica, si prega di riavviarla manualmente",
|
||||
"about12": "Se abilitato, consentirà l'accesso ad altri dispositivi nella stessa rete locale. Dopo la modifica l'app si chiuderà automaticamente, riavviare manualmente",
|
||||
"networkServeTLS": "Abilita HTTPS",
|
||||
"networkServeTLSTip": "Se abilitato, le connessioni di rete utilizzeranno un certificato autofirmato generato automaticamente per la crittografia TLS. Il browser mostrerà un avviso di sicurezza che deve essere accettato manualmente. Dopo la modifica l'app si chiuderà automaticamente, riavviare manualmente",
|
||||
"networkServeTLSTip2": "HTTPS è attivo solo sulla porta fissa 6806, le porte casuali non supportano HTTPS",
|
||||
"exportCACert": "Esporta certificato CA",
|
||||
"exportCACertTip": "Esporta il certificato CA(ca.crt). Installa questo certificato sui dispositivi client per fidarsi della connessione HTTPS autofirmata",
|
||||
"exportCABundle": "Esporta bundle CA",
|
||||
"exportCABundleTip": "Esporta il certificato CA e la chiave privata per condividerli con altri dispositivi SiYuan. I dispositivi che usano lo stesso CA si fideranno dei certificati emessi da esso",
|
||||
"importCABundle": "Importa bundle CA",
|
||||
"importCABundleTip": "Importa il bundle CA da un altro dispositivo SiYuan. Dopo l'importazione questo dispositivo userà lo stesso CA, consentendo ai client di fidarsi dei certificati di tutti i dispositivi",
|
||||
"importCABundleSuccess": "Bundle CA importato con successo, riavviare l'app per applicare le modifiche",
|
||||
"about13": "Token API",
|
||||
"about14": "Il token deve essere autenticato quando si chiama l'API<br>Intestazione della richiesta HTTP <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "Non abilitare il proxy quando è impostato su <code class='fn__code'>Connessione diretta</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "システムのロック画面に従う",
|
||||
"about8": "システム画面のロック時にアプリケーションを自動的にロックします",
|
||||
"about11": "ネットワークサービス",
|
||||
"about12": "同じ LAN 内の他のデバイスからのアクセスを許可します。設定を変更するとアプリケーションが自動的に終了しますので手動で再起動してください",
|
||||
"about12": "有効にすると同じローカルネットワーク内の他のデバイスからアクセスできるようになります。変更後、自動的にアプリが終了するため手動で再起動してください",
|
||||
"networkServeTLS": "HTTPS を有効にする",
|
||||
"networkServeTLSTip": "有効にするとネットワーク接続は自動生成された自己署名証明書で TLS による暗号化が行われます。ブラウザにセキュリティ警告が表示され、手動で受け入れる必要があります。変更後、自動的にアプリが終了するため手動で再起動してください",
|
||||
"networkServeTLSTip2": "HTTPS は固定ポート 6806 のみで有効であり、ランダムポートでは HTTPS はサポートされません",
|
||||
"exportCACert": "CA 証明書をエクスポート",
|
||||
"exportCACertTip": "CA 証明書(ca.crt)ファイルをエクスポートします。この証明書をクライアント端末にインストールして、自己署名の HTTPS 接続を信頼してください",
|
||||
"exportCABundle": "CA バンドルをエクスポート",
|
||||
"exportCABundleTip": "他の SiYuan デバイスと共有するために CA 証明書と秘密鍵をエクスポートします。同じ CA を使用するすべてのデバイスは、この CA により発行された証明書を信頼します",
|
||||
"importCABundle": "CA バンドルをインポート",
|
||||
"importCABundleTip": "別の SiYuan デバイスから CA バンドルをインポートします。インポート後、このデバイスは同じ CA を使用し、クライアントがすべてのデバイスの証明書を信頼できるようになります",
|
||||
"importCABundleSuccess": "CA バンドルのインポートに成功しました。変更を適用するにはアプリを再起動してください",
|
||||
"about13": "API トークン",
|
||||
"about14": "API を呼び出す際にトークンを認証する必要があります<br>HTTP リクエストヘッダー <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "<code class='fn__code'>直接接続</code>に設定されている場合はプロキシを有効にしないでください",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "시스템 잠금 화면 따르기",
|
||||
"about8": "활성화하면 시스템 화면이 잠길 때 애플리케이션이 자동으로 잠깁니다",
|
||||
"about11": "네트워크 서빙",
|
||||
"about12": "활성화하면 동일한 LAN에 있는 다른 장치의 액세스가 허용됩니다. 수정 후 애플리케이션이 자동으로 닫히므로 수동으로 다시 시작하세요",
|
||||
"about12": "활성화하면 동일한 로컬 네트워크의 다른 장치에서 접근할 수 있습니다. 변경 후 애플리케이션이 자동으로 종료되니 수동으로 재시작하십시오",
|
||||
"networkServeTLS": "HTTPS 사용",
|
||||
"networkServeTLSTip": "활성화하면 네트워크 연결은 자동 생성된 자체 서명 인증서를 사용해 TLS로 암호화됩니다. 브라우저에서 보안 경고가 표시되며 수동으로 수락해야 합니다. 변경 후 애플리케이션이 자동으로 종료되니 수동으로 재시작하십시오",
|
||||
"networkServeTLSTip2": "HTTPS는 고정 포트 6806에서만 작동하며, 임의 포트는 HTTPS를 지원하지 않습니다",
|
||||
"exportCACert": "CA 인증서 내보내기",
|
||||
"exportCACertTip": "CA 인증서(ca.crt)파일을 내보냅니다. 클라이언트 장치에 이 인증서를 설치하여 자체 서명된 HTTPS 연결을 신뢰하도록 하십시오",
|
||||
"exportCABundle": "CA 번들 내보내기",
|
||||
"exportCABundleTip": "CA 인증서와 개인 키를 내보내어 다른 SiYuan 장치와 공유합니다. 동일한 CA를 사용하는 모든 장치는 해당 CA에서 발급한 인증서를 신뢰합니다",
|
||||
"importCABundle": "CA 번들 가져오기",
|
||||
"importCABundleTip": "다른 SiYuan 장치에서 CA 번들을 가져옵니다. 가져온 후 이 장치는 동일한 CA를 사용하게 되어 클라이언트가 모든 장치의 인증서를 신뢰할 수 있게 됩니다",
|
||||
"importCABundleSuccess": "CA 번들 가져오기에 성공했습니다. 변경 사항을 적용하려면 애플리케이션을 재시작하세요",
|
||||
"about13": "API 토큰",
|
||||
"about14": "API를 호출할 때 토큰을 인증해야 합니다<br>HTTP 요청 헤더 <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "<code class='fn__code'>직접 연결</code>로 설정하면 프록시를 활성화하지 않습니다",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Podążaj za ekranem blokady systemu",
|
||||
"about8": "Po włączeniu, aplikacja zostanie automatycznie zablokowana przy zablokowaniu ekranu systemu",
|
||||
"about11": "Usługa sieciowa",
|
||||
"about12": "Po włączeniu, inne urządzenia w tej samej sieci LAN będą miały możliwość dostępu. Aplikacja zostanie automatycznie zamknięta po modyfikacji, proszę uruchomić ponownie ręcznie",
|
||||
"about12": "Po włączeniu umożliwi dostęp innym urządzeniom w tej samej sieci lokalnej. Po zmianie aplikacja zostanie automatycznie zamknięta, proszę ręcznie ponownie uruchomić",
|
||||
"networkServeTLS": "Włącz HTTPS",
|
||||
"networkServeTLSTip": "Po włączeniu połączenia sieciowe będą szyfrowane przy użyciu automatycznie wygenerowanego certyfikatu samopodpisanego dla TLS. Przeglądarka wyświetli ostrzeżenie bezpieczeństwa, które należy zaakceptować ręcznie. Po zmianie aplikacja zostanie automatycznie zamknięta, proszę ręcznie ponownie uruchomić",
|
||||
"networkServeTLSTip2": "HTTPS działa tylko na stałym porcie 6806, losowe porty nie obsługują HTTPS",
|
||||
"exportCACert": "Eksportuj certyfikat CA",
|
||||
"exportCACertTip": "Eksportuj certyfikat CA(ca.crt). Zainstaluj ten certyfikat na urządzeniach klienckich, aby ufać samopodpisanemu połączeniu HTTPS",
|
||||
"exportCABundle": "Eksportuj pakiet CA",
|
||||
"exportCABundleTip": "Eksportuj certyfikat CA i klucz prywatny, aby udostępnić je innym urządzeniom SiYuan. Urządzenia używające tego samego CA będą ufać certyfikatom wystawionym przez niego",
|
||||
"importCABundle": "Importuj pakiet CA",
|
||||
"importCABundleTip": "Importuj pakiet CA z innego urządzenia SiYuan. Po imporcie to urządzenie będzie używać tego samego CA, pozwalając klientom ufać certyfikatom wszystkich urządzeń",
|
||||
"importCABundleSuccess": "Pakiet CA zaimportowano pomyślnie, uruchom aplikację ponownie, aby zastosować zmiany",
|
||||
"about13": "Token API",
|
||||
"about14": "Token potrzebny do autoryzacji podczas wywoływania API<br>Nagłówek żądania HTTP <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "Nie włączaj proxy, gdy ustawione na <code class='fn__code'>Bezpośrednie połączenie</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Seguir bloqueio de tela do sistema",
|
||||
"about8": "Quando ativado, o aplicativo será bloqueado automaticamente ao bloquear a tela do sistema",
|
||||
"about11": "Serviço de rede",
|
||||
"about12": "Quando ativado, outros dispositivos na mesma LAN terão permissão para acessar. O aplicativo será fechado automaticamente após a modificação, por favor reinicie manualmente",
|
||||
"about12": "Ao ativar, permitirá que outros dispositivos na mesma rede local acessem. Após a alteração, o aplicativo será fechado automaticamente, por favor reinicie manualmente",
|
||||
"networkServeTLS": "Ativar HTTPS",
|
||||
"networkServeTLSTip": "Ao ativar, as conexões de rede usarão um certificado autoassinado gerado automaticamente para criptografia TLS. O navegador exibirá um aviso de segurança que precisa ser aceito manualmente. Após a alteração, o aplicativo será fechado automaticamente, por favor reinicie manualmente",
|
||||
"networkServeTLSTip2": "HTTPS funciona apenas na porta fixa 6806, portas aleatórias não suportam HTTPS",
|
||||
"exportCACert": "Exportar certificado CA",
|
||||
"exportCACertTip": "Exportar o certificado CA(ca.crt). Instale este certificado nos dispositivos clientes para confiar na conexão HTTPS autoassinada",
|
||||
"exportCABundle": "Exportar pacote CA",
|
||||
"exportCABundleTip": "Exportar o certificado CA e a chave privada para compartilhar com outros dispositivos SiYuan. Dispositivos que usam o mesmo CA confiarão nos certificados emitidos por ele",
|
||||
"importCABundle": "Importar pacote CA",
|
||||
"importCABundleTip": "Importar o pacote CA de outro dispositivo SiYuan. Após a importação, este dispositivo usará o mesmo CA, permitindo que clientes confiem nos certificados de todos os dispositivos",
|
||||
"importCABundleSuccess": "Pacote CA importado com sucesso, por favor reinicie o aplicativo para aplicar as alterações",
|
||||
"about13": "Token API",
|
||||
"about14": "O token precisa ser autenticado ao chamar a API<br>Cabeçalho da requisição HTTP <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "Não ativar proxy quando definido como <code class='fn__code'>Conexão direta</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Следовать блокировке системы",
|
||||
"about8": "После включения программа будет автоматически блокироваться при блокировке системного экрана",
|
||||
"about11": "Сетевое обслуживание",
|
||||
"about12": "После включения другие устройства в одной локальной сети смогут получить доступ. Программа будет автоматически закрыта после изменения, пожалуйста, перезапустите вручную",
|
||||
"about12": "Включение позволит другим устройствам в той же локальной сети получить доступ. После изменения приложение будет автоматически закрыто, перезапустите вручную",
|
||||
"networkServeTLS": "Включить HTTPS",
|
||||
"networkServeTLSTip": "После включения сетевые соединения будут защищены TLS с автоматически сгенерированным самоподписанным сертификатом. Браузер покажет предупреждение о безопасности, его нужно принять вручную. После изменения приложение будет автоматически закрыто, перезапустите вручную",
|
||||
"networkServeTLSTip2": "HTTPS действует только на фиксированном порту 6806, на случайных портах HTTPS не поддерживается",
|
||||
"exportCACert": "Экспортировать CA‑сертификат",
|
||||
"exportCACertTip": "Экспортировать CA‑сертификат(ca.crt). Установите этот сертификат на клиентские устройства, чтобы доверять самоподписанному HTTPS‑соединению",
|
||||
"exportCABundle": "Экспортировать пакет CA",
|
||||
"exportCABundleTip": "Экспортировать CA‑сертификат и приватный ключ для обмена с другими устройствами SiYuan. Устройства, использующие тот же CA, будут доверять этим сертификатам",
|
||||
"importCABundle": "Импортировать пакет CA",
|
||||
"importCABundleTip": "Импортировать пакет CA с другого устройства SiYuan. После импорта это устройство будет использовать тот же CA, позволяя клиентам доверять сертификатам всех таких устройств",
|
||||
"importCABundleSuccess": "Пакет CA успешно импортирован, перезапустите приложение для применения изменений",
|
||||
"about13": "API токен",
|
||||
"about14": "Токен необходимо аутентифицировать при вызове API<br>HTTP заголовок запроса <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "Не включайте прокси, когда установлено <code class='fn__code'>Прямое подключение</code>",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "Sistem ekran kilidini takip et",
|
||||
"about8": "Etkinleştirildiğinde sistem ekranı kilitlendiğinde uygulama otomatik olarak kilitlenir",
|
||||
"about11": "Ağ paylaşımı",
|
||||
"about12": "Etkinleştirildiğinde aynı yerel ağdaki diğer cihazlar erişebilir. Değişiklikten sonra uygulama otomatik kapanır, lütfen elle yeniden başlat",
|
||||
"about12": "Etkinleştirildiğinde aynı yerel ağdaki diğer cihazların erişimine izin verilir. Değişiklikten sonra uygulama otomatik olarak kapanacaktır, lütfen elle yeniden başlatın",
|
||||
"networkServeTLS": "HTTPS'i etkinleştir",
|
||||
"networkServeTLSTip": "Etkinleştirildiğinde ağ bağlantıları otomatik oluşturulan kendi imzalı sertifika ile TLS şifrelemesi kullanacaktır. Tarayıcı güvenlik uyarısı gösterecektir; elle kabul etmeniz gerekir. Değişiklikten sonra uygulama otomatik olarak kapanacaktır, lütfen elle yeniden başlatın",
|
||||
"networkServeTLSTip2": "HTTPS yalnızca sabit port 6806'da geçerlidir, rastgele portlar HTTPS'i desteklemez",
|
||||
"exportCACert": "CA Sertifikasını Dışa Aktar",
|
||||
"exportCACertTip": "CA sertifikasını(ca.crt)dosyası olarak dışa aktarın. Bu sertifikayı istemci cihazlara yükleyerek kendi imzalı HTTPS bağlantısını güvenilir kılın",
|
||||
"exportCABundle": "CA Sertifika Paketi Dışa Aktar",
|
||||
"exportCABundleTip": "CA sertifikası ve özel anahtarı diğer SiYuan cihazlarıyla paylaşmak için dışa aktarın. Aynı CA'yı kullanan tüm cihazlar bu CA tarafından verilen sertifikalara güvenecektir",
|
||||
"importCABundle": "CA Sertifika Paketi İçe Aktar",
|
||||
"importCABundleTip": "CA sertifika paketini başka bir SiYuan cihazından içe aktarın. İçe aktarıldıktan sonra bu cihaz aynı CA'yı kullanacak ve istemcilerin tüm cihazların sertifikalarına güvenmesine izin verecektir",
|
||||
"importCABundleSuccess": "CA sertifika paketi başarıyla içe aktarıldı, değişiklikleri uygulamak için lütfen uygulamayı yeniden başlatın",
|
||||
"about13": "API anahtarı",
|
||||
"about14": "API çağrılarında kimlik doğrulaması gerekir<br>HTTP başlığı: <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "<code class='fn__code'>Doğrudan bağlantı</code> seçiliyse proxy etkinleştirme",
|
||||
|
|
|
|||
|
|
@ -1297,7 +1297,17 @@
|
|||
"about7": "跟隨系統鎖屏",
|
||||
"about8": "啟用後將會在系統鎖屏時自動鎖定應用",
|
||||
"about11": "網路伺服",
|
||||
"about12": "啟用後將允許同一區域網內的其他設備進行存取。修改後會自動關閉應用,請手動重啟",
|
||||
"about12": "啟用後將允許同一區域網路內的其他設備進行訪問。修改後會自動關閉應用,請手動重啟",
|
||||
"networkServeTLS": "啟用 HTTPS",
|
||||
"networkServeTLSTip": "啟用後網路連線將使用自動生成的自簽名憑證進行 TLS 加密。瀏覽器會顯示安全警告,需要手動接受。修改後會自動關閉應用,請手動重啟",
|
||||
"networkServeTLSTip2": "HTTPS 僅在固定埠 6806 上生效,隨機埠不支援 HTTPS",
|
||||
"exportCACert": "匯出 CA 憑證",
|
||||
"exportCACertTip": "匯出 CA 憑證(ca.crt)檔案。將此憑證安裝到客戶端裝置以信任自簽名 HTTPS 連線",
|
||||
"exportCABundle": "匯出 CA 憑證包",
|
||||
"exportCABundleTip": "匯出 CA 憑證與私鑰以便與其他思源裝置共享。使用相同 CA 的所有裝置將信任該 CA 所簽發的憑證",
|
||||
"importCABundle": "匯入 CA 憑證包",
|
||||
"importCABundleTip": "從另一台思源裝置匯入 CA 憑證包。匯入後,此裝置將使用相同的 CA,允許客戶端信任所有該 CA 的裝置所簽發的憑證",
|
||||
"importCABundleSuccess": "CA 憑證包匯入成功,請重啟應用以應用更改",
|
||||
"about13": "API token",
|
||||
"about14": "調用 API 時需要通過該 token 進行鑒權<br>HTTP 請求標頭 <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "設置為 <code class='fn__code'>直接連接</code> 時不啟用代理",
|
||||
|
|
|
|||
|
|
@ -1298,6 +1298,16 @@
|
|||
"about8": "启用后将会在系统锁屏时自动锁定应用",
|
||||
"about11": "网络伺服",
|
||||
"about12": "启用后将允许同一局域网内的其他设备进行访问。修改后会自动关闭应用,请手动重启",
|
||||
"networkServeTLS": "启用 HTTPS",
|
||||
"networkServeTLSTip": "启用后网络连接将使用自动生成的自签名证书进行 TLS 加密。浏览器会显示安全警告,需要手动接受。修改后会自动关闭应用,请手动重启",
|
||||
"networkServeTLSTip2": "HTTPS 仅在固定端口 6806 上生效,随机端口不支持 HTTPS",
|
||||
"exportCACert": "导出 CA 证书",
|
||||
"exportCACertTip": "导出 CA 证书(ca.crt)文件。将此证书安装到客户端设备以信任自签名 HTTPS 连接",
|
||||
"exportCABundle": "导出 CA 证书包",
|
||||
"exportCABundleTip": "导出 CA 证书和私钥以便与其他思源设备共享。使用相同 CA 的所有设备将被导入该证书的客户端信任",
|
||||
"importCABundle": "导入 CA 证书包",
|
||||
"importCABundleTip": "从另一台思源设备导入 CA 证书包。导入后,此设备将使用相同的 CA,允许客户端信任所有设备的证书",
|
||||
"importCABundleSuccess": "CA 证书包导入成功,请重启应用以应用更改",
|
||||
"about13": "API token",
|
||||
"about14": "调用 API 时需要通过该 token 进行鉴权<br>HTTP 请求标头 <code class=\"fn__code\">Authorization: token ${token}</code>",
|
||||
"about17": "设置为 <code class='fn__code'>直接连接</code> 时不启用代理",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,45 @@ export const about = {
|
|||
<div class="fn__space"></div>
|
||||
<input class="b3-switch fn__flex-center" id="networkServe" type="checkbox"${window.siyuan.config.system.networkServe ? " checked" : ""}>
|
||||
</label>
|
||||
<label class="b3-label fn__flex">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.networkServeTLS}
|
||||
<div class="b3-label__text">${window.siyuan.languages.networkServeTLSTip}</div>
|
||||
<div class="b3-label__text">${window.siyuan.languages.networkServeTLSTip2}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<input class="b3-switch fn__flex-center" id="networkServeTLS" type="checkbox"${window.siyuan.config.system.networkServeTLS ? " checked" : ""}${!window.siyuan.config.system.networkServe ? " disabled" : ""}>
|
||||
</label>
|
||||
<div class="fn__flex b3-label config__item${window.siyuan.config.system.networkServeTLS ? "" : " fn__none"}" id="exportCACertSection">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.exportCACert}
|
||||
<div class="b3-label__text">${window.siyuan.languages.exportCACertTip}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<button class="b3-button b3-button--outline fn__size200 fn__flex-center" id="exportCACert">
|
||||
<svg><use xlink:href="#iconUpload"></use></svg>${window.siyuan.languages.export}
|
||||
</button>
|
||||
</div>
|
||||
<div class="fn__flex b3-label config__item${window.siyuan.config.system.networkServeTLS ? "" : " fn__none"}" id="exportCABundleSection">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.exportCABundle}
|
||||
<div class="b3-label__text">${window.siyuan.languages.exportCABundleTip}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<button class="b3-button b3-button--outline fn__size200 fn__flex-center" id="exportCABundle">
|
||||
<svg><use xlink:href="#iconUpload"></use></svg>${window.siyuan.languages.export}
|
||||
</button>
|
||||
</div>
|
||||
<div class="fn__flex b3-label config__item${window.siyuan.config.system.networkServeTLS ? "" : " fn__none"}" id="importCABundleSection">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.importCABundle}
|
||||
<div class="b3-label__text">${window.siyuan.languages.importCABundleTip}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<button class="b3-button b3-button--outline fn__size200 fn__flex-center" id="importCABundle">
|
||||
<svg><use xlink:href="#iconDownload"></use></svg>${window.siyuan.languages.import}
|
||||
</button>
|
||||
</div>
|
||||
<div class="b3-label${(window.siyuan.config.readonly || (isBrowser() && !isInIOS() && !isInAndroid() && !isIPad() && !isInHarmony())) ? " fn__none" : ""}">
|
||||
<div class="fn__flex">
|
||||
<div class="fn__flex-1">
|
||||
|
|
@ -102,7 +141,7 @@ export const about = {
|
|||
<div class="b3-label__text">${window.siyuan.languages.about18}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<button data-type="open" data-url="${window.siyuan.config.system.networkServe ? window.siyuan.config.serverAddrs[0] : "http://127.0.0.1:"+ location.port}" class="b3-button b3-button--outline fn__size200 fn__flex-center">
|
||||
<button data-type="open" data-url="${"http://127.0.0.1:"+ location.port}" class="b3-button b3-button--outline fn__size200 fn__flex-center">
|
||||
<svg><use xlink:href="#iconLink"></use></svg>${window.siyuan.languages.about4}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -379,7 +418,12 @@ ${checkUpdateHTML}
|
|||
});
|
||||
});
|
||||
const networkServeElement = about.element.querySelector("#networkServe") as HTMLInputElement;
|
||||
const networkServeTLSElement = about.element.querySelector("#networkServeTLS") as HTMLInputElement;
|
||||
networkServeElement.addEventListener("change", () => {
|
||||
networkServeTLSElement.disabled = !networkServeElement.checked;
|
||||
if (!networkServeElement.checked) {
|
||||
networkServeTLSElement.checked = false;
|
||||
}
|
||||
fetchPost("/api/system/setNetworkServe", {networkServe: networkServeElement.checked}, () => {
|
||||
exportLayout({
|
||||
errorExit: true,
|
||||
|
|
@ -387,6 +431,60 @@ ${checkUpdateHTML}
|
|||
});
|
||||
});
|
||||
});
|
||||
networkServeTLSElement.addEventListener("change", () => {
|
||||
const exportCACertSection = about.element.querySelector("#exportCACertSection");
|
||||
const exportCABundleSection = about.element.querySelector("#exportCABundleSection");
|
||||
const importCABundleSection = about.element.querySelector("#importCABundleSection");
|
||||
if (exportCACertSection && exportCABundleSection && importCABundleSection) {
|
||||
if (networkServeTLSElement.checked) {
|
||||
exportCACertSection.classList.remove("fn__none");
|
||||
exportCABundleSection.classList.remove("fn__none");
|
||||
importCABundleSection.classList.remove("fn__none");
|
||||
} else {
|
||||
exportCACertSection.classList.add("fn__none");
|
||||
exportCABundleSection.classList.add("fn__none");
|
||||
importCABundleSection.classList.add("fn__none");
|
||||
}
|
||||
}
|
||||
fetchPost("/api/system/setNetworkServeTLS", {networkServeTLS: networkServeTLSElement.checked}, () => {
|
||||
exportLayout({
|
||||
errorExit: true,
|
||||
cb: exitSiYuan
|
||||
});
|
||||
});
|
||||
});
|
||||
about.element.querySelector("#exportCACert")?.addEventListener("click", () => {
|
||||
fetchPost("/api/system/exportTLSCACert", {}, (response) => {
|
||||
openByMobile(response.data.path);
|
||||
});
|
||||
});
|
||||
about.element.querySelector("#exportCABundle")?.addEventListener("click", () => {
|
||||
fetchPost("/api/system/exportTLSCABundle", {}, (response) => {
|
||||
openByMobile(response.data.path);
|
||||
});
|
||||
});
|
||||
about.element.querySelector("#importCABundle")?.addEventListener("click", () => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".zip";
|
||||
input.onchange = () => {
|
||||
if (input.files && input.files[0]) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", input.files[0]);
|
||||
fetch("/api/system/importTLSCABundle", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
}).then(res => res.json()).then((response) => {
|
||||
if (response.code === 0) {
|
||||
showMessage(window.siyuan.languages.importCABundleSuccess);
|
||||
} else {
|
||||
showMessage(response.msg, 6000, "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
});
|
||||
const lockScreenModeElement = about.element.querySelector("#lockScreenMode") as HTMLInputElement;
|
||||
lockScreenModeElement.addEventListener("change", () => {
|
||||
fetchPost("/api/system/setFollowSystemLockScreen", {lockScreenMode: lockScreenModeElement.checked ? 1 : 0}, () => {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,43 @@ export const initAbout = () => {
|
|||
<div class="fn__space"></div>
|
||||
<input class="b3-switch fn__flex-center" id="networkServe" type="checkbox"${window.siyuan.config.system.networkServe ? " checked" : ""}>
|
||||
</label>
|
||||
<label class="b3-label fn__flex${window.siyuan.config.readonly ? " fn__none" : ""}">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.networkServeTLS}
|
||||
<div class="b3-label__text">${window.siyuan.languages.networkServeTLSTip}</div>
|
||||
<div class="b3-label__text">${window.siyuan.languages.networkServeTLSTip2}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<input class="b3-switch fn__flex-center" id="networkServeTLS" type="checkbox"${window.siyuan.config.system.networkServeTLS ? " checked" : ""}${!window.siyuan.config.system.networkServe ? " disabled" : ""}>
|
||||
</label>
|
||||
<div class="b3-label${window.siyuan.config.system.networkServeTLS ? "" : " fn__none"}" id="exportCACertSection">
|
||||
${window.siyuan.languages.exportCACert}
|
||||
<div class="fn__hr"></div>
|
||||
<button class="b3-button b3-button--outline fn__block" id="exportCACert">
|
||||
<svg><use xlink:href="#iconUpload"></use></svg>${window.siyuan.languages.export}
|
||||
</button>
|
||||
<div class="b3-label__text">${window.siyuan.languages.exportCACertTip}</div>
|
||||
</div>
|
||||
<div class="b3-label${window.siyuan.config.system.networkServeTLS ? "" : " fn__none"}" id="exportCABundleSection">
|
||||
${window.siyuan.languages.exportCABundle}
|
||||
<div class="fn__hr"></div>
|
||||
<button class="b3-button b3-button--outline fn__block" id="exportCABundle">
|
||||
<svg><use xlink:href="#iconUpload"></use></svg>${window.siyuan.languages.export}
|
||||
</button>
|
||||
<div class="b3-label__text">${window.siyuan.languages.exportCABundleTip}</div>
|
||||
</div>
|
||||
<div class="b3-label${window.siyuan.config.system.networkServeTLS ? "" : " fn__none"}" id="importCABundleSection">
|
||||
${window.siyuan.languages.importCABundle}
|
||||
<div class="fn__hr"></div>
|
||||
<button class="b3-button b3-button--outline fn__block" id="importCABundle">
|
||||
<svg><use xlink:href="#iconDownload"></use></svg>${window.siyuan.languages.import}
|
||||
</button>
|
||||
<div class="b3-label__text">${window.siyuan.languages.importCABundleTip}</div>
|
||||
</div>
|
||||
<div class="b3-label">
|
||||
${window.siyuan.languages.about2}
|
||||
<div class="fn__hr"></div>
|
||||
<a target="_blank" href="${window.siyuan.config.system.networkServe ? window.siyuan.config.serverAddrs[0] : "http://127.0.0.1:" + location.port}" class="b3-button b3-button--outline fn__block">
|
||||
<a target="_blank" href="${"http://127.0.0.1:" + location.port}" class="b3-button b3-button--outline fn__block">
|
||||
<svg><use xlink:href="#iconLink"></use></svg>${window.siyuan.languages.about4}
|
||||
</a>
|
||||
<div class="b3-label__text">${window.siyuan.languages.about3.replace("${port}", location.port)}</div>
|
||||
|
|
@ -451,11 +484,67 @@ export const initAbout = () => {
|
|||
});
|
||||
});
|
||||
const networkServeElement = modelMainElement.querySelector("#networkServe") as HTMLInputElement;
|
||||
const networkServeTLSElement = modelMainElement.querySelector("#networkServeTLS") as HTMLInputElement;
|
||||
networkServeElement.addEventListener("change", () => {
|
||||
networkServeTLSElement.disabled = !networkServeElement.checked;
|
||||
if (!networkServeElement.checked) {
|
||||
networkServeTLSElement.checked = false;
|
||||
}
|
||||
fetchPost("/api/system/setNetworkServe", {networkServe: networkServeElement.checked}, () => {
|
||||
exitSiYuan();
|
||||
});
|
||||
});
|
||||
networkServeTLSElement.addEventListener("change", () => {
|
||||
const exportCACertSection = modelMainElement.querySelector("#exportCACertSection");
|
||||
const exportCABundleSection = modelMainElement.querySelector("#exportCABundleSection");
|
||||
const importCABundleSection = modelMainElement.querySelector("#importCABundleSection");
|
||||
if (exportCACertSection && exportCABundleSection && importCABundleSection) {
|
||||
if (networkServeTLSElement.checked) {
|
||||
exportCACertSection.classList.remove("fn__none");
|
||||
exportCABundleSection.classList.remove("fn__none");
|
||||
importCABundleSection.classList.remove("fn__none");
|
||||
} else {
|
||||
exportCACertSection.classList.add("fn__none");
|
||||
exportCABundleSection.classList.add("fn__none");
|
||||
importCABundleSection.classList.add("fn__none");
|
||||
}
|
||||
}
|
||||
fetchPost("/api/system/setNetworkServeTLS", {networkServeTLS: networkServeTLSElement.checked}, () => {
|
||||
exitSiYuan();
|
||||
});
|
||||
});
|
||||
modelMainElement.querySelector("#exportCACert")?.addEventListener("click", () => {
|
||||
fetchPost("/api/system/exportTLSCACert", {}, (response) => {
|
||||
openByMobile(response.data.path);
|
||||
});
|
||||
});
|
||||
modelMainElement.querySelector("#exportCABundle")?.addEventListener("click", () => {
|
||||
fetchPost("/api/system/exportTLSCABundle", {}, (response) => {
|
||||
openByMobile(response.data.path);
|
||||
});
|
||||
});
|
||||
modelMainElement.querySelector("#importCABundle")?.addEventListener("click", () => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".zip";
|
||||
input.onchange = () => {
|
||||
if (input.files && input.files[0]) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", input.files[0]);
|
||||
fetch("/api/system/importTLSCABundle", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
}).then(res => res.json()).then((response) => {
|
||||
if (response.code === 0) {
|
||||
showMessage(window.siyuan.languages.importCABundleSuccess);
|
||||
} else {
|
||||
showMessage(response.msg, 6000, "error");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
});
|
||||
const tokenElement = modelMainElement.querySelector("#token") as HTMLInputElement;
|
||||
tokenElement.addEventListener("change", () => {
|
||||
fetchPost("/api/system/setAPIToken", {token: tokenElement.value}, () => {
|
||||
|
|
|
|||
4
app/src/types/config.d.ts
vendored
4
app/src/types/config.d.ts
vendored
|
|
@ -1623,6 +1623,10 @@ declare namespace Config {
|
|||
* Whether to enable network serve (whether to allow connections from other devices)
|
||||
*/
|
||||
networkServe: boolean;
|
||||
/**
|
||||
* Whether to enable HTTPS for network serve (TLS encryption)
|
||||
*/
|
||||
networkServeTLS: boolean;
|
||||
/**
|
||||
* The operating system name determined at compile time (obtained using the command `go tool
|
||||
* dist list`)
|
||||
|
|
|
|||
|
|
@ -349,9 +349,7 @@ func getUnusedAssets(c *gin.Context) {
|
|||
util.PushMsg(fmt.Sprintf(model.Conf.Language(251), total, maxUnusedAssets), 5000)
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"unusedAssets": unusedAssets,
|
||||
}
|
||||
ret.Data = unusedAssets
|
||||
}
|
||||
|
||||
func getMissingAssets(c *gin.Context) {
|
||||
|
|
@ -359,9 +357,7 @@ func getMissingAssets(c *gin.Context) {
|
|||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
missingAssets := model.MissingAssets()
|
||||
ret.Data = map[string]interface{}{
|
||||
"missingAssets": missingAssets,
|
||||
}
|
||||
ret.Data = missingAssets
|
||||
}
|
||||
|
||||
func resolveAssetPath(c *gin.Context) {
|
||||
|
|
|
|||
|
|
@ -67,9 +67,7 @@ func getUnusedAttributeViews(c *gin.Context) {
|
|||
util.PushMsg(fmt.Sprintf(model.Conf.Language(279), total, maxUnusedAttributeViews), 5000)
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"unusedAttributeViews": unusedAttributeViews,
|
||||
}
|
||||
ret.Data = unusedAttributeViews
|
||||
}
|
||||
|
||||
func getAttributeViewItemIDsByBoundIDs(c *gin.Context) {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ func ServeAPI(ginServer *gin.Engine) {
|
|||
ginServer.Handle("POST", "/api/system/setAccessAuthCode", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setAccessAuthCode)
|
||||
ginServer.Handle("POST", "/api/system/setFollowSystemLockScreen", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setFollowSystemLockScreen)
|
||||
ginServer.Handle("POST", "/api/system/setNetworkServe", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setNetworkServe)
|
||||
ginServer.Handle("POST", "/api/system/setNetworkServeTLS", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setNetworkServeTLS)
|
||||
ginServer.Handle("POST", "/api/system/exportTLSCACert", model.CheckAuth, model.CheckAdminRole, exportTLSCACert)
|
||||
ginServer.Handle("POST", "/api/system/exportTLSCABundle", model.CheckAuth, model.CheckAdminRole, exportTLSCABundle)
|
||||
ginServer.Handle("POST", "/api/system/importTLSCABundle", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, importTLSCABundle)
|
||||
ginServer.Handle("POST", "/api/system/setAutoLaunch", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setAutoLaunch)
|
||||
ginServer.Handle("POST", "/api/system/setDownloadInstallPkg", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setDownloadInstallPkg)
|
||||
ginServer.Handle("POST", "/api/system/setNetworkProxy", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setNetworkProxy)
|
||||
|
|
|
|||
|
|
@ -720,6 +720,173 @@ func setNetworkServe(c *gin.Context) {
|
|||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
func setNetworkServeTLS(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
networkServeTLS := arg["networkServeTLS"].(bool)
|
||||
model.Conf.System.NetworkServeTLS = networkServeTLS
|
||||
model.Conf.Save()
|
||||
|
||||
util.PushMsg(model.Conf.Language(42), 1000*15)
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
func exportTLSCACert(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
caCertPath := filepath.Join(util.ConfDir, util.TLSCACertFilename)
|
||||
if !gulu.File.IsExist(caCertPath) {
|
||||
ret.Code = -1
|
||||
ret.Msg = "CA certificate not found"
|
||||
return
|
||||
}
|
||||
|
||||
tmpDir := filepath.Join(util.TempDir, "export")
|
||||
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
exportPath := filepath.Join(tmpDir, util.TLSCACertFilename)
|
||||
if err := gulu.File.CopyFile(caCertPath, exportPath); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"path": "/export/" + util.TLSCACertFilename,
|
||||
}
|
||||
}
|
||||
|
||||
func exportTLSCABundle(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
caCertPath := filepath.Join(util.ConfDir, util.TLSCACertFilename)
|
||||
caKeyPath := filepath.Join(util.ConfDir, util.TLSCAKeyFilename)
|
||||
|
||||
if !gulu.File.IsExist(caCertPath) || !gulu.File.IsExist(caKeyPath) {
|
||||
ret.Code = -1
|
||||
ret.Msg = "CA certificate not found, please enable TLS first"
|
||||
return
|
||||
}
|
||||
|
||||
tmpDir := filepath.Join(util.TempDir, "export", "ca-bundle")
|
||||
os.RemoveAll(tmpDir)
|
||||
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
if err := gulu.File.CopyFile(caCertPath, filepath.Join(tmpDir, util.TLSCACertFilename)); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := gulu.File.CopyFile(caKeyPath, filepath.Join(tmpDir, util.TLSCAKeyFilename)); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
zipPath := filepath.Join(util.TempDir, "export", "ca-bundle.zip")
|
||||
zipFile, err := gulu.Zip.Create(zipPath)
|
||||
if err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if err := zipFile.AddDirectory("", tmpDir); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if err := zipFile.Close(); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"path": "/export/ca-bundle.zip",
|
||||
}
|
||||
}
|
||||
|
||||
func importTLSCABundle(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = "file is required: " + err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
tmpDir := filepath.Join(util.TempDir, "import")
|
||||
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
tmpZipPath := filepath.Join(tmpDir, "ca-bundle.zip")
|
||||
if err := c.SaveUploadedFile(file, tmpZipPath); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpZipPath)
|
||||
|
||||
extractDir := filepath.Join(tmpDir, "ca-bundle")
|
||||
os.RemoveAll(extractDir)
|
||||
if err := gulu.Zip.Unzip(tmpZipPath, extractDir); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = "failed to extract zip file: " + err.Error()
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(extractDir)
|
||||
|
||||
caCertPath := filepath.Join(extractDir, util.TLSCACertFilename)
|
||||
caCertPEM, err := os.ReadFile(caCertPath)
|
||||
if err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = "ca.crt not found in zip file"
|
||||
return
|
||||
}
|
||||
|
||||
caKeyPath := filepath.Join(extractDir, util.TLSCAKeyFilename)
|
||||
caKeyPEM, err := os.ReadFile(caKeyPath)
|
||||
if err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = "ca.key not found in zip file"
|
||||
return
|
||||
}
|
||||
|
||||
if err := util.ImportCABundle(string(caCertPEM), string(caKeyPEM)); err != nil {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"msg": "CA bundle imported successfully. Please restart to apply changes.",
|
||||
}
|
||||
}
|
||||
|
||||
func setAutoLaunch(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ type System struct {
|
|||
ConfDir string `json:"confDir"`
|
||||
DataDir string `json:"dataDir"`
|
||||
|
||||
NetworkServe bool `json:"networkServe"` // 是否开启网络伺服
|
||||
NetworkProxy *NetworkProxy `json:"networkProxy"`
|
||||
NetworkServe bool `json:"networkServe"` // 是否开启网络伺服
|
||||
NetworkServeTLS bool `json:"networkServeTLS"` // 是否开启 HTTPS 网络伺服
|
||||
NetworkProxy *NetworkProxy `json:"networkProxy"`
|
||||
|
||||
DownloadInstallPkg bool `json:"downloadInstallPkg"`
|
||||
AutoLaunch2 int `json:"autoLaunch2"` // 0:不自动启动,1:自动启动,2:自动启动+隐藏主窗口
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ require (
|
|||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.4 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/teambition/rrule-go v1.8.2 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
|
|
|
|||
|
|
@ -395,6 +395,8 @@ github.com/siyuan-note/logging v0.0.0-20260117134552-88b424dfe7f1 h1:C2Y1XhBrrGe
|
|||
github.com/siyuan-note/logging v0.0.0-20260117134552-88b424dfe7f1/go.mod h1:t3Tmt3DgQx0zqJmrckszJ+JBZ7iJrD1Ktp8FDBQ249E=
|
||||
github.com/siyuan-note/riff v0.0.0-20251022131846-228528e70754 h1:6QYpy7s5HlRSge09TyM/mT0vz1RDcWYZdkxEh7hmbH4=
|
||||
github.com/siyuan-note/riff v0.0.0-20251022131846-228528e70754/go.mod h1:/N7+N2CsZ0nleNPpP3b+06Bzqvuhy6GUmLY7Kug/zT0=
|
||||
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||
|
|
@ -486,6 +488,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
|
|
@ -509,6 +512,7 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
|
|||
|
|
@ -737,7 +737,8 @@ func RemoveUnusedAssets() (ret []string) {
|
|||
}
|
||||
|
||||
var hashes []string
|
||||
for _, p := range unusedAssets {
|
||||
for _, unusedAsset := range unusedAssets {
|
||||
p := unusedAsset.Item
|
||||
historyPath := filepath.Join(historyDir, p)
|
||||
if p = filepath.Join(util.DataDir, p); filelock.IsExist(p) {
|
||||
if filelock.IsHidden(p) {
|
||||
|
|
@ -757,7 +758,8 @@ func RemoveUnusedAssets() (ret []string) {
|
|||
sql.BatchRemoveAssetsQueue(hashes)
|
||||
|
||||
for _, unusedAsset := range unusedAssets {
|
||||
absPath := filepath.Join(util.DataDir, unusedAsset)
|
||||
p := unusedAsset.Item
|
||||
absPath := filepath.Join(util.DataDir, p)
|
||||
if filelock.IsExist(absPath) {
|
||||
info, statErr := os.Stat(absPath)
|
||||
if statErr == nil {
|
||||
|
|
@ -779,7 +781,7 @@ func RemoveUnusedAssets() (ret []string) {
|
|||
return
|
||||
}
|
||||
|
||||
util.RemoveAssetText(unusedAsset)
|
||||
util.RemoveAssetText(p)
|
||||
}
|
||||
ret = append(ret, absPath)
|
||||
}
|
||||
|
|
@ -965,9 +967,14 @@ func RenameAsset(oldPath, newName string) (newPath string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func UnusedAssets() (ret []string) {
|
||||
type UnusedItem struct {
|
||||
Item string `json:"item"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func UnusedAssets() (ret []*UnusedItem) {
|
||||
defer logging.Recover()
|
||||
ret = []string{}
|
||||
ret = []*UnusedItem{}
|
||||
|
||||
assetsPathMap, err := allAssetAbsPaths()
|
||||
if err != nil {
|
||||
|
|
@ -1121,15 +1128,15 @@ func UnusedAssets() (ret []string) {
|
|||
if strings.HasPrefix(p, "/") {
|
||||
p = p[1:]
|
||||
}
|
||||
ret = append(ret, p)
|
||||
name := util.RemoveID(path.Base(p))
|
||||
ret = append(ret, &UnusedItem{Item: p, Name: name})
|
||||
}
|
||||
sort.Strings(ret)
|
||||
return
|
||||
}
|
||||
|
||||
func MissingAssets() (ret []string) {
|
||||
func MissingAssets() (ret []*UnusedItem) {
|
||||
defer logging.Recover()
|
||||
ret = []string{}
|
||||
ret = []*UnusedItem{}
|
||||
|
||||
assetsPathMap, err := allAssetAbsPaths()
|
||||
if err != nil {
|
||||
|
|
@ -1197,17 +1204,17 @@ func MissingAssets() (ret []string) {
|
|||
if strings.HasPrefix(dest, "assets/.") {
|
||||
// Assets starting with `.` should not be considered missing assets https://github.com/siyuan-note/siyuan/issues/8821
|
||||
if !filelock.IsExist(filepath.Join(util.DataDir, dest)) {
|
||||
ret = append(ret, dest)
|
||||
name := util.RemoveID(path.Base(dest))
|
||||
ret = append(ret, &UnusedItem{Item: dest, Name: name})
|
||||
}
|
||||
} else {
|
||||
ret = append(ret, dest)
|
||||
name := util.RemoveID(path.Base(dest))
|
||||
ret = append(ret, &UnusedItem{Item: dest, Name: name})
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(ret)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func RemoveUnusedAttributeViews() (ret []string) {
|
|||
}
|
||||
|
||||
for _, unusedAv := range unusedAttributeViews {
|
||||
id := unusedAv["id"].(string)
|
||||
id := unusedAv.Item
|
||||
srcPath := filepath.Join(util.DataDir, "storage", "av", id+".json")
|
||||
if filelock.IsExist(srcPath) {
|
||||
historyPath := filepath.Join(historyDir, "storage", "av", id+".json")
|
||||
|
|
@ -108,7 +108,7 @@ func RemoveUnusedAttributeViews() (ret []string) {
|
|||
}
|
||||
|
||||
for _, unusedAv := range unusedAttributeViews {
|
||||
id := unusedAv["id"].(string)
|
||||
id := unusedAv.Item
|
||||
absPath := filepath.Join(util.DataDir, "storage", "av", id+".json")
|
||||
if filelock.IsExist(absPath) {
|
||||
info, statErr := os.Stat(absPath)
|
||||
|
|
@ -132,9 +132,9 @@ func RemoveUnusedAttributeViews() (ret []string) {
|
|||
return
|
||||
}
|
||||
|
||||
func UnusedAttributeViews() (ret []map[string]any) {
|
||||
func UnusedAttributeViews() (ret []*UnusedItem) {
|
||||
defer logging.Recover()
|
||||
ret = []map[string]any{}
|
||||
ret = []*UnusedItem{}
|
||||
|
||||
allAvIDs, err := getAllAvIDs()
|
||||
if err != nil {
|
||||
|
|
@ -172,16 +172,9 @@ func UnusedAttributeViews() (ret []map[string]any) {
|
|||
for _, id := range allAvIDs {
|
||||
if !docReferencedAvIDs[id] && !isRelatedSrcAvDocReferenced(id, docReferencedAvIDs, checkedAvIDs) {
|
||||
name, _ := av.GetAttributeViewName(id)
|
||||
ret = append(ret, map[string]any{
|
||||
"id": id,
|
||||
"name": name,
|
||||
})
|
||||
ret = append(ret, &UnusedItem{Item: id, Name: name})
|
||||
}
|
||||
}
|
||||
|
||||
if 1 > len(ret) {
|
||||
ret = []map[string]any{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,25 +17,80 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/siyuan-note/logging"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
"github.com/soheilhy/cmux"
|
||||
)
|
||||
|
||||
func InitFixedPortService(host string) {
|
||||
func InitFixedPortService(host string, useTLS bool, certPath, keyPath string) {
|
||||
if util.FixedPort != util.ServerPort {
|
||||
if util.IsPortOpen(util.FixedPort) {
|
||||
return
|
||||
}
|
||||
|
||||
addr := host + ":" + util.FixedPort
|
||||
|
||||
// 启动一个固定 6806 端口的反向代理服务器,这样浏览器扩展才能直接使用 127.0.0.1:6806,不用配置端口
|
||||
proxy := httputil.NewSingleHostReverseProxy(util.ServerURL)
|
||||
logging.LogInfof("fixed port service [%s:%s] is running", host, util.FixedPort)
|
||||
if proxyErr := http.ListenAndServe(host+":"+util.FixedPort, proxy); nil != proxyErr {
|
||||
logging.LogWarnf("boot fixed port service [%s] failed: %s", util.ServerURL, proxyErr)
|
||||
|
||||
if useTLS {
|
||||
proxy.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
logging.LogInfof("fixed port service [%s] is running (HTTP/HTTPS dual mode)", addr)
|
||||
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
logging.LogWarnf("boot fixed port service [%s] failed: %s", addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
m := cmux.New(ln)
|
||||
|
||||
// Match TLS connections (first byte 0x16 indicates TLS handshake)
|
||||
tlsL := m.Match(cmux.TLS())
|
||||
// Match HTTP (anything else)
|
||||
httpL := m.Match(cmux.Any())
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||
if err != nil {
|
||||
logging.LogWarnf("failed to load TLS cert for fixed port service: %s", err)
|
||||
ln.Close()
|
||||
return
|
||||
}
|
||||
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||
|
||||
tlsListener := tls.NewListener(tlsL, tlsConfig)
|
||||
|
||||
go func() {
|
||||
httpServer := &http.Server{Handler: proxy}
|
||||
if err := httpServer.Serve(httpL); err != nil && err != cmux.ErrListenerClosed {
|
||||
logging.LogWarnf("fixed port HTTP server error: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
httpsServer := &http.Server{Handler: proxy}
|
||||
if err := httpsServer.Serve(tlsListener); err != nil && err != cmux.ErrListenerClosed {
|
||||
logging.LogWarnf("fixed port HTTPS server error: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := m.Serve(); err != nil && err != cmux.ErrListenerClosed {
|
||||
logging.LogWarnf("fixed port cmux serve error: %s", err)
|
||||
}
|
||||
} else {
|
||||
logging.LogInfof("fixed port service [%s] is running", addr)
|
||||
if proxyErr := http.ListenAndServe(addr, proxy); nil != proxyErr {
|
||||
logging.LogWarnf("boot fixed port service [%s] failed: %s", util.ServerURL, proxyErr)
|
||||
}
|
||||
}
|
||||
logging.LogInfof("fixed port service [%s:%s] is stopped", host, util.FixedPort)
|
||||
logging.LogInfof("fixed port service [%s] is stopped", addr)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,14 +210,32 @@ func Serve(fastMode bool, cookieKey string) {
|
|||
if !fastMode {
|
||||
rewritePortJSON(pid, port)
|
||||
}
|
||||
logging.LogInfof("kernel [pid=%s] http server [%s] is booting", pid, host+":"+port)
|
||||
|
||||
// Prepare TLS if enabled
|
||||
var certPath, keyPath string
|
||||
useTLS := model.Conf.System.NetworkServeTLS && model.Conf.System.NetworkServe
|
||||
if useTLS {
|
||||
// Ensure TLS certificates exist (proxy will use them directly)
|
||||
var tlsErr error
|
||||
certPath, keyPath, tlsErr = util.GetOrCreateTLSCert()
|
||||
if tlsErr != nil {
|
||||
logging.LogErrorf("failed to get TLS certificates: %s", tlsErr)
|
||||
if !fastMode {
|
||||
os.Exit(logging.ExitCodeUnavailablePort)
|
||||
}
|
||||
return
|
||||
}
|
||||
logging.LogInfof("kernel [pid=%s] http server [%s] is booting (TLS will be enabled on fixed port proxy)", pid, host+":"+port)
|
||||
} else {
|
||||
logging.LogInfof("kernel [pid=%s] http server [%s] is booting", pid, host+":"+port)
|
||||
}
|
||||
util.HttpServing = true
|
||||
|
||||
go util.HookUILoaded()
|
||||
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
go proxy.InitFixedPortService(host)
|
||||
go proxy.InitFixedPortService(host, useTLS, certPath, keyPath)
|
||||
go proxy.InitPublishService()
|
||||
// 反代服务器启动失败不影响核心服务器启动
|
||||
}()
|
||||
|
|
|
|||
337
kernel/util/cert.go
Normal file
337
kernel/util/cert.go
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
// SiYuan - Refactor your thinking
|
||||
// Copyright (c) 2020-present, b3log.org
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/siyuan-note/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
TLSCACertFilename = "ca.crt"
|
||||
TLSCAKeyFilename = "ca.key"
|
||||
TLSCertFilename = "cert.pem"
|
||||
TLSKeyFilename = "key.pem"
|
||||
)
|
||||
|
||||
// Returns paths to existing TLS certificates or generates new ones signed by a local CA.
|
||||
// Certificates are stored in the conf directory of the workspace.
|
||||
func GetOrCreateTLSCert() (certPath, keyPath string, err error) {
|
||||
certPath = filepath.Join(ConfDir, TLSCertFilename)
|
||||
keyPath = filepath.Join(ConfDir, TLSKeyFilename)
|
||||
caCertPath := filepath.Join(ConfDir, TLSCACertFilename)
|
||||
caKeyPath := filepath.Join(ConfDir, TLSCAKeyFilename)
|
||||
|
||||
if !gulu.File.IsExist(caCertPath) || !gulu.File.IsExist(caKeyPath) {
|
||||
logging.LogInfof("generating local CA for TLS...")
|
||||
if err = generateCACert(caCertPath, caKeyPath); err != nil {
|
||||
logging.LogErrorf("failed to generate CA certificates: %s", err)
|
||||
return "", "", err
|
||||
}
|
||||
}
|
||||
|
||||
if gulu.File.IsExist(certPath) && gulu.File.IsExist(keyPath) {
|
||||
if validateCert(certPath) {
|
||||
logging.LogInfof("using existing TLS certificates from [%s]", ConfDir)
|
||||
return certPath, keyPath, nil
|
||||
}
|
||||
logging.LogInfof("existing TLS certificates are invalid or expired, regenerating...")
|
||||
}
|
||||
|
||||
caCert, caKey, err := loadCA(caCertPath, caKeyPath)
|
||||
if err != nil {
|
||||
logging.LogErrorf("failed to load CA certificates: %s", err)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
logging.LogInfof("generating TLS server certificates signed by local CA...")
|
||||
if err = generateServerCert(certPath, keyPath, caCert, caKey); err != nil {
|
||||
logging.LogErrorf("failed to generate TLS certificates: %s", err)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
logging.LogInfof("generated TLS certificates at [%s]", ConfDir)
|
||||
return certPath, keyPath, nil
|
||||
}
|
||||
|
||||
// Checks if the certificate file exists, is not expired, and contains all current IP addresses
|
||||
func validateCert(certPath string) bool {
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if certificate is still valid, with 7 day buffer
|
||||
if !time.Now().Add(7 * 24 * time.Hour).Before(cert.NotAfter) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if certificate contains all current IP addresses
|
||||
currentIPs := GetServerAddrs()
|
||||
certIPMap := make(map[string]bool)
|
||||
for _, ip := range cert.IPAddresses {
|
||||
certIPMap[ip.String()] = true
|
||||
}
|
||||
|
||||
for _, ipStr := range currentIPs {
|
||||
ipStr = trimIPv6Brackets(ipStr)
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !certIPMap[ip.String()] {
|
||||
logging.LogInfof("certificate missing current IP address [%s], will regenerate", ip.String())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Creates a new self-signed CA certificate
|
||||
func generateCACert(certPath, keyPath string) error {
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(10 * 365 * 24 * time.Hour)
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"SiYuan"},
|
||||
CommonName: "SiYuan Local CA",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeCertAndKey(certPath, keyPath, certDER, privateKey)
|
||||
}
|
||||
|
||||
// Creates a new server certificate signed by the CA
|
||||
func generateServerCert(certPath, keyPath string, caCert *x509.Certificate, caKey any) error {
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||
|
||||
ipAddresses := []net.IP{
|
||||
net.ParseIP("127.0.0.1"),
|
||||
net.IPv6loopback,
|
||||
}
|
||||
|
||||
localIPs := GetServerAddrs()
|
||||
for _, ipStr := range localIPs {
|
||||
ipStr = trimIPv6Brackets(ipStr)
|
||||
if ip := net.ParseIP(ipStr); ip != nil {
|
||||
ipAddresses = append(ipAddresses, ip)
|
||||
}
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"SiYuan"},
|
||||
CommonName: "SiYuan Local Server",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{"localhost"},
|
||||
IPAddresses: ipAddresses,
|
||||
}
|
||||
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, caCert, &privateKey.PublicKey, caKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeCertAndKey(certPath, keyPath, certDER, privateKey)
|
||||
}
|
||||
|
||||
// Loads the CA certificate and private key from files
|
||||
func loadCA(certPath, keyPath string) (*x509.Certificate, any, error) {
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode CA certificate PEM")
|
||||
}
|
||||
|
||||
caCert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
keyPEM, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
block, _ = pem.Decode(keyPEM)
|
||||
if block == nil {
|
||||
return nil, nil, fmt.Errorf("failed to decode CA key PEM")
|
||||
}
|
||||
|
||||
caKey, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return caCert, caKey, nil
|
||||
}
|
||||
|
||||
func writeCertAndKey(certPath, keyPath string, certDER []byte, privateKey *ecdsa.PrivateKey) error {
|
||||
certFile, err := os.Create(certPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer certFile.Close()
|
||||
|
||||
if err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certDER}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyFile, err := os.Create(keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer keyFile.Close()
|
||||
|
||||
keyDER, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Imports a CA certificate and private key from PEM-encoded strings.
|
||||
func ImportCABundle(caCertPEM, caKeyPEM string) error {
|
||||
certBlock, _ := pem.Decode([]byte(caCertPEM))
|
||||
if certBlock == nil {
|
||||
return fmt.Errorf("failed to decode CA certificate PEM")
|
||||
}
|
||||
|
||||
caCert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse CA certificate: %w", err)
|
||||
}
|
||||
|
||||
if !caCert.IsCA {
|
||||
return fmt.Errorf("the provided certificate is not a CA certificate")
|
||||
}
|
||||
|
||||
keyBlock, _ := pem.Decode([]byte(caKeyPEM))
|
||||
if keyBlock == nil {
|
||||
return fmt.Errorf("failed to decode CA private key PEM")
|
||||
}
|
||||
|
||||
_, err = x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse CA private key: %w", err)
|
||||
}
|
||||
|
||||
caCertPath := filepath.Join(ConfDir, TLSCACertFilename)
|
||||
caKeyPath := filepath.Join(ConfDir, TLSCAKeyFilename)
|
||||
|
||||
if err := os.WriteFile(caCertPath, []byte(caCertPEM), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write CA certificate: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(caKeyPath, []byte(caKeyPEM), 0600); err != nil {
|
||||
return fmt.Errorf("failed to write CA private key: %w", err)
|
||||
}
|
||||
|
||||
certPath := filepath.Join(ConfDir, TLSCertFilename)
|
||||
keyPath := filepath.Join(ConfDir, TLSKeyFilename)
|
||||
|
||||
if gulu.File.IsExist(certPath) {
|
||||
os.Remove(certPath)
|
||||
}
|
||||
if gulu.File.IsExist(keyPath) {
|
||||
os.Remove(keyPath)
|
||||
}
|
||||
|
||||
logging.LogInfof("imported CA bundle, server certificate will be regenerated on next TLS initialization")
|
||||
return nil
|
||||
}
|
||||
|
||||
// trimIPv6Brackets removes brackets from IPv6 address strings like "[::1]"
|
||||
func trimIPv6Brackets(ip string) string {
|
||||
if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' {
|
||||
return ip[1 : len(ip)-1]
|
||||
}
|
||||
return ip
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue