feat: add Ngrok tunnel provider (#1415)
Adds Ngrok quick tunnel support Adds desktop tunnel docs across locales Improves tunnel settings provider and TTL labels
This commit is contained in:
committed by
GitHub
parent
25ed8e6abb
commit
00a7807168
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: Desktop Tunnels
|
||||||
|
description: Create Cloudflare or Ngrok tunnels from the desktop app.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Desktop Tunnels
|
||||||
|
|
||||||
|
The desktop app can create a public tunnel from **Settings → OpenChamber → Tunnel**. You do not need to start OpenChamber from the CLI for this path.
|
||||||
|
|
||||||
|
## Install a provider
|
||||||
|
|
||||||
|
OpenChamber starts the provider CLI on your machine. Install the provider you want to use first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare uses `cloudflared`. Ngrok requires an ngrok account and an authtoken from the ngrok dashboard:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Start from the app
|
||||||
|
|
||||||
|
1. Open **Settings → OpenChamber → Tunnel**.
|
||||||
|
2. Choose **Cloudflare** or **Ngrok**.
|
||||||
|
3. Start a quick tunnel.
|
||||||
|
4. Scan the generated QR code from your phone.
|
||||||
|
|
||||||
|
Ngrok currently supports quick tunnels. Cloudflare supports quick tunnels and managed Cloudflare modes.
|
||||||
|
|
||||||
|
## Access protection
|
||||||
|
|
||||||
|
Even when the provider URL itself is public, OpenChamber still protects access with its own connect token. The generated connect link contains a one-time token, has a TTL, and old unused links are revoked when you generate a new one or stop/restart the tunnel.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Tunnels](/tunnels/) — CLI tunnel usage and managed Cloudflare modes
|
||||||
|
- [PWA & Mobile](/mobile/) — reach OpenChamber from your phone
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: Túneles de escritorio
|
||||||
|
description: Crea túneles de Cloudflare o Ngrok desde la app de escritorio.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Túneles de escritorio
|
||||||
|
|
||||||
|
La app de escritorio puede crear un túnel público desde **Settings → OpenChamber → Tunnel**. No necesitas iniciar OpenChamber desde el CLI para este flujo.
|
||||||
|
|
||||||
|
## Instala un proveedor
|
||||||
|
|
||||||
|
OpenChamber inicia el CLI del proveedor en tu máquina. Instala primero el proveedor que quieras usar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare usa `cloudflared`. Ngrok requiere una cuenta de ngrok y un authtoken del panel de ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inicia desde la app
|
||||||
|
|
||||||
|
1. Abre **Settings → OpenChamber → Tunnel**.
|
||||||
|
2. Elige **Cloudflare** o **Ngrok**.
|
||||||
|
3. Inicia un quick tunnel.
|
||||||
|
4. Escanea el código QR generado desde tu teléfono.
|
||||||
|
|
||||||
|
Ngrok actualmente soporta quick tunnels. Cloudflare soporta quick tunnels y modos gestionados de Cloudflare.
|
||||||
|
|
||||||
|
## Protección de acceso
|
||||||
|
|
||||||
|
Aunque la URL del proveedor sea pública, OpenChamber sigue protegiendo el acceso con su propio connect token. El enlace generado contiene un token de un solo uso, tiene TTL, y los enlaces antiguos sin usar se revocan cuando generas uno nuevo o detienes/reinicias el túnel.
|
||||||
|
|
||||||
|
## Relacionado
|
||||||
|
|
||||||
|
- [Túneles](/es/tunnels/) — uso de túneles por CLI y modos gestionados de Cloudflare
|
||||||
|
- [PWA y acceso móvil](/es/mobile/) — accede a OpenChamber desde tu teléfono
|
||||||
@@ -7,7 +7,22 @@ description: Expón OpenChamber de forma segura para acceso remoto y móvil.
|
|||||||
|
|
||||||
Un túnel es un enlace público a tu OpenChamber, para que puedas acceder a él desde tu teléfono u otra red. Usa `openchamber tunnel` para crear uno para una instancia en marcha.
|
Un túnel es un enlace público a tu OpenChamber, para que puedas acceder a él desde tu teléfono u otra red. Usa `openchamber tunnel` para crear uno para una instancia en marcha.
|
||||||
|
|
||||||
## Inicio rápido (modo rápido de Cloudflare)
|
## Requisitos previos
|
||||||
|
|
||||||
|
OpenChamber inicia el CLI del proveedor de túnel en tu máquina. Instala primero el proveedor que quieras usar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Los túneles rápidos de Cloudflare pueden funcionar con `cloudflared`. Ngrok requiere una cuenta de ngrok y un authtoken del panel de ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inicio rápido
|
||||||
|
|
||||||
1. Inicia OpenChamber:
|
1. Inicia OpenChamber:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ Un túnel es un enlace público a tu OpenChamber, para que puedas acceder a él
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Inicia un túnel:
|
2. Inicia un túnel de Cloudflare:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
O inicia un túnel de Ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. Comprueba el estado:
|
3. Comprueba el estado:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ Cuando el túnel está activo, `status` muestra una URL pública. Ábrela, o esc
|
|||||||
|
|
||||||
Por defecto, OpenChamber imprime un código QR en sesiones TTY interactivas. Usa `--qr` para forzar la salida del QR, o `--no-qr` para desactivarla.
|
Por defecto, OpenChamber imprime un código QR en sesiones TTY interactivas. Usa `--qr` para forzar la salida del QR, o `--no-qr` para desactivarla.
|
||||||
|
|
||||||
|
## Proveedores
|
||||||
|
|
||||||
|
- `cloudflare`: modos quick, managed remote y managed local
|
||||||
|
- `ngrok`: modo quick
|
||||||
|
|
||||||
## Modos gestionados
|
## Modos gestionados
|
||||||
|
|
||||||
### Gestionado remoto
|
### Gestionado remoto
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## Relacionado
|
## Relacionado
|
||||||
|
|
||||||
- [Seguridad](/es/security/) — protege la interfaz antes de exponerla
|
- [Seguridad](/es/security/) — protege la interfaz antes de exponerla
|
||||||
|
- [Túneles de escritorio](/es/desktop-tunnels/) — configuración de túneles en la app de escritorio sin iniciar desde CLI
|
||||||
- [PWA y acceso móvil](/es/mobile/) — accede a OpenChamber desde tu teléfono
|
- [PWA y acceso móvil](/es/mobile/) — accede a OpenChamber desde tu teléfono
|
||||||
- [Resolución de problemas](/es/troubleshooting/) — si el enlace del túnel no funciona
|
- [Resolución de problemas](/es/troubleshooting/) — si el enlace del túnel no funciona
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: 데스크톱 터널
|
||||||
|
description: 데스크톱 앱에서 Cloudflare 또는 Ngrok 터널을 만드세요.
|
||||||
|
---
|
||||||
|
|
||||||
|
# 데스크톱 터널
|
||||||
|
|
||||||
|
데스크톱 앱은 **Settings → OpenChamber → Tunnel**에서 공개 터널을 만들 수 있습니다. 이 경로에서는 OpenChamber를 CLI로 시작할 필요가 없습니다.
|
||||||
|
|
||||||
|
## 공급자 설치
|
||||||
|
|
||||||
|
OpenChamber는 사용자의 컴퓨터에서 공급자 CLI를 실행합니다. 먼저 사용할 공급자를 설치하세요:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare는 `cloudflared`를 사용합니다. Ngrok은 ngrok 계정과 ngrok 대시보드의 authtoken이 필요합니다:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 앱에서 시작
|
||||||
|
|
||||||
|
1. **Settings → OpenChamber → Tunnel**을 엽니다.
|
||||||
|
2. **Cloudflare** 또는 **Ngrok**을 선택합니다.
|
||||||
|
3. quick tunnel을 시작합니다.
|
||||||
|
4. 휴대폰에서 생성된 QR 코드를 스캔합니다.
|
||||||
|
|
||||||
|
Ngrok은 현재 quick tunnels를 지원합니다. Cloudflare는 quick tunnels와 Cloudflare 관리형 모드를 지원합니다.
|
||||||
|
|
||||||
|
## 접근 보호
|
||||||
|
|
||||||
|
공급자 URL 자체가 공개되어 있어도 OpenChamber는 자체 connect token으로 접근을 보호합니다. 생성된 연결 링크에는 일회용 token이 들어 있고 TTL이 있으며, 새 링크를 만들거나 터널을 중지/재시작하면 이전의 사용되지 않은 링크는 무효화됩니다.
|
||||||
|
|
||||||
|
## 관련 문서
|
||||||
|
|
||||||
|
- [터널](/ko/tunnels/) — CLI 터널 사용법과 Cloudflare 관리형 모드
|
||||||
|
- [PWA 및 모바일 접속](/ko/mobile/) — 휴대폰에서 OpenChamber에 접속하세요
|
||||||
@@ -7,7 +7,22 @@ description: 원격 및 모바일 접근을 위해 OpenChamber를 안전하게
|
|||||||
|
|
||||||
터널은 OpenChamber로 연결되는 공개 링크로, 휴대폰이나 다른 네트워크에서 접근할 수 있게 해줍니다. 실행 중인 인스턴스에 터널을 만들려면 `openchamber tunnel`을 사용하세요.
|
터널은 OpenChamber로 연결되는 공개 링크로, 휴대폰이나 다른 네트워크에서 접근할 수 있게 해줍니다. 실행 중인 인스턴스에 터널을 만들려면 `openchamber tunnel`을 사용하세요.
|
||||||
|
|
||||||
## 빠른 시작 (Cloudflare 빠른 모드)
|
## 사전 요구 사항
|
||||||
|
|
||||||
|
OpenChamber는 사용자의 컴퓨터에서 터널 공급자 CLI를 실행합니다. 먼저 사용할 공급자를 설치하세요:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare 빠른 터널은 `cloudflared`로 실행할 수 있습니다. Ngrok은 ngrok 계정과 ngrok 대시보드의 authtoken이 필요합니다:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 빠른 시작
|
||||||
|
|
||||||
1. OpenChamber를 시작합니다:
|
1. OpenChamber를 시작합니다:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ description: 원격 및 모바일 접근을 위해 OpenChamber를 안전하게
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 터널을 시작합니다:
|
2. Cloudflare 터널을 시작합니다:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
또는 Ngrok 터널을 시작합니다:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. 상태를 확인합니다:
|
3. 상태를 확인합니다:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ openchamber tunnel status
|
|||||||
|
|
||||||
기본적으로 OpenChamber는 대화형 TTY 세션에서 QR 코드를 출력합니다. `--qr`로 QR 출력을 강제하거나 `--no-qr`로 비활성화하세요.
|
기본적으로 OpenChamber는 대화형 TTY 세션에서 QR 코드를 출력합니다. `--qr`로 QR 출력을 강제하거나 `--no-qr`로 비활성화하세요.
|
||||||
|
|
||||||
|
## 공급자
|
||||||
|
|
||||||
|
- `cloudflare`: quick, managed remote, managed local 모드
|
||||||
|
- `ngrok`: quick 모드
|
||||||
|
|
||||||
## 관리형 모드
|
## 관리형 모드
|
||||||
|
|
||||||
### 관리형 원격
|
### 관리형 원격
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## 관련 문서
|
## 관련 문서
|
||||||
|
|
||||||
- [보안](/ko/security/) — 외부에 공개하기 전에 UI를 보호하세요
|
- [보안](/ko/security/) — 외부에 공개하기 전에 UI를 보호하세요
|
||||||
|
- [데스크톱 터널](/ko/desktop-tunnels/) — CLI 시작 없이 데스크톱 앱에서 터널 설정
|
||||||
- [PWA 및 모바일 접속](/ko/mobile/) — 휴대폰에서 OpenChamber에 접속하세요
|
- [PWA 및 모바일 접속](/ko/mobile/) — 휴대폰에서 OpenChamber에 접속하세요
|
||||||
- [문제 해결](/ko/troubleshooting/) — 터널 링크가 작동하지 않을 때
|
- [문제 해결](/ko/troubleshooting/) — 터널 링크가 작동하지 않을 때
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: Tunele w aplikacji desktopowej
|
||||||
|
description: Twórz tunele Cloudflare lub Ngrok z aplikacji desktopowej.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tunele w aplikacji desktopowej
|
||||||
|
|
||||||
|
Aplikacja desktopowa może utworzyć publiczny tunel z poziomu **Settings → OpenChamber → Tunnel**. W tym trybie nie musisz uruchamiać OpenChamber z CLI.
|
||||||
|
|
||||||
|
## Zainstaluj dostawcę
|
||||||
|
|
||||||
|
OpenChamber uruchamia CLI dostawcy na Twoim komputerze. Najpierw zainstaluj dostawcę, którego chcesz użyć:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare używa `cloudflared`. Ngrok wymaga konta ngrok i authtokena z panelu ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uruchom z aplikacji
|
||||||
|
|
||||||
|
1. Otwórz **Settings → OpenChamber → Tunnel**.
|
||||||
|
2. Wybierz **Cloudflare** albo **Ngrok**.
|
||||||
|
3. Uruchom quick tunnel.
|
||||||
|
4. Zeskanuj wygenerowany kod QR telefonem.
|
||||||
|
|
||||||
|
Ngrok obsługuje teraz quick tunnels. Cloudflare obsługuje quick tunnels i zarządzane tryby Cloudflare.
|
||||||
|
|
||||||
|
## Ochrona dostępu
|
||||||
|
|
||||||
|
Nawet jeśli URL dostawcy jest publiczny, OpenChamber nadal chroni dostęp własnym connect tokenem. Wygenerowany link zawiera jednorazowy token, ma TTL, a stare nieużyte linki są unieważniane po wygenerowaniu nowego linku albo zatrzymaniu/restarcie tunelu.
|
||||||
|
|
||||||
|
## Powiązane
|
||||||
|
|
||||||
|
- [Tunele](/pl/tunnels/) — użycie tuneli przez CLI i zarządzane tryby Cloudflare
|
||||||
|
- [PWA i dostęp z telefonu](/pl/mobile/) — korzystaj z OpenChamber z telefonu
|
||||||
@@ -7,7 +7,22 @@ description: Bezpiecznie udostępnij OpenChamber do dostępu zdalnego i mobilneg
|
|||||||
|
|
||||||
Tunel to publiczny link do Twojego OpenChamber, dzięki któremu możesz dotrzeć do niego z telefonu lub z innej sieci. Użyj `openchamber tunnel`, aby utworzyć go dla działającej instancji.
|
Tunel to publiczny link do Twojego OpenChamber, dzięki któremu możesz dotrzeć do niego z telefonu lub z innej sieci. Użyj `openchamber tunnel`, aby utworzyć go dla działającej instancji.
|
||||||
|
|
||||||
## Szybki start (tryb szybki Cloudflare)
|
## Wymagania wstępne
|
||||||
|
|
||||||
|
OpenChamber uruchamia CLI dostawcy tunelu na Twoim komputerze. Najpierw zainstaluj dostawcę, którego chcesz użyć:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Szybkie tunele Cloudflare działają przez `cloudflared`. Ngrok wymaga konta ngrok i authtokena z panelu ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Szybki start
|
||||||
|
|
||||||
1. Uruchom OpenChamber:
|
1. Uruchom OpenChamber:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ Tunel to publiczny link do Twojego OpenChamber, dzięki któremu możesz dotrze
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Uruchom tunel:
|
2. Uruchom tunel Cloudflare:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Albo uruchom tunel Ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. Sprawdź status:
|
3. Sprawdź status:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ Gdy tunel działa, `status` pokazuje publiczny adres URL. Otwórz go lub zeskanu
|
|||||||
|
|
||||||
Domyślnie OpenChamber wypisuje kod QR w interaktywnych sesjach TTY. Użyj `--qr`, aby wymusić wyświetlenie kodu QR, lub `--no-qr`, aby je wyłączyć.
|
Domyślnie OpenChamber wypisuje kod QR w interaktywnych sesjach TTY. Użyj `--qr`, aby wymusić wyświetlenie kodu QR, lub `--no-qr`, aby je wyłączyć.
|
||||||
|
|
||||||
|
## Dostawcy
|
||||||
|
|
||||||
|
- `cloudflare`: tryby quick, managed remote i managed local
|
||||||
|
- `ngrok`: tryb quick
|
||||||
|
|
||||||
## Tryby zarządzane
|
## Tryby zarządzane
|
||||||
|
|
||||||
### Zarządzany zdalny
|
### Zarządzany zdalny
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## Powiązane
|
## Powiązane
|
||||||
|
|
||||||
- [Bezpieczeństwo](/pl/security/) — zabezpiecz interfejs przed udostępnieniem
|
- [Bezpieczeństwo](/pl/security/) — zabezpiecz interfejs przed udostępnieniem
|
||||||
|
- [Tunele w aplikacji desktopowej](/pl/desktop-tunnels/) — konfiguracja tunelu w aplikacji desktopowej bez startu z CLI
|
||||||
- [PWA i dostęp z telefonu](/pl/mobile/) — korzystaj z OpenChamber z telefonu
|
- [PWA i dostęp z telefonu](/pl/mobile/) — korzystaj z OpenChamber z telefonu
|
||||||
- [Rozwiązywanie problemów](/pl/troubleshooting/) — jeśli link tunelu nie działa
|
- [Rozwiązywanie problemów](/pl/troubleshooting/) — jeśli link tunelu nie działa
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: Túneis no desktop
|
||||||
|
description: Crie túneis Cloudflare ou Ngrok pelo app desktop.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Túneis no desktop
|
||||||
|
|
||||||
|
O app desktop pode criar um túnel público em **Settings → OpenChamber → Tunnel**. Você não precisa iniciar o OpenChamber pelo CLI para esse fluxo.
|
||||||
|
|
||||||
|
## Instale um provedor
|
||||||
|
|
||||||
|
O OpenChamber inicia o CLI do provedor na sua máquina. Instale primeiro o provedor que você quer usar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
A Cloudflare usa `cloudflared`. O Ngrok exige uma conta ngrok e um authtoken do painel do ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inicie pelo app
|
||||||
|
|
||||||
|
1. Abra **Settings → OpenChamber → Tunnel**.
|
||||||
|
2. Escolha **Cloudflare** ou **Ngrok**.
|
||||||
|
3. Inicie um quick tunnel.
|
||||||
|
4. Escaneie o QR code gerado pelo celular.
|
||||||
|
|
||||||
|
O Ngrok atualmente suporta quick tunnels. A Cloudflare suporta quick tunnels e modos gerenciados da Cloudflare.
|
||||||
|
|
||||||
|
## Proteção de acesso
|
||||||
|
|
||||||
|
Mesmo quando a URL do provedor é pública, o OpenChamber ainda protege o acesso com seu próprio connect token. O link gerado contém um token de uso único, tem TTL, e links antigos não utilizados são revogados quando você gera um novo ou para/reinicia o túnel.
|
||||||
|
|
||||||
|
## Relacionado
|
||||||
|
|
||||||
|
- [Túneis](/pt-br/tunnels/) — uso de túneis pelo CLI e modos gerenciados da Cloudflare
|
||||||
|
- [PWA e Acesso Móvel](/pt-br/mobile/) — acesse o OpenChamber pelo celular
|
||||||
@@ -7,7 +7,22 @@ description: Exponha o OpenChamber com segurança para acesso remoto e móvel.
|
|||||||
|
|
||||||
Um túnel é um link público para o seu OpenChamber, para que você possa acessá-lo pelo celular ou de outra rede. Use `openchamber tunnel` para criar um para uma instância em execução.
|
Um túnel é um link público para o seu OpenChamber, para que você possa acessá-lo pelo celular ou de outra rede. Use `openchamber tunnel` para criar um para uma instância em execução.
|
||||||
|
|
||||||
## Início rápido (modo rápido da Cloudflare)
|
## Pré-requisitos
|
||||||
|
|
||||||
|
O OpenChamber inicia o CLI do provedor de túnel na sua máquina. Instale primeiro o provedor que você quer usar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Os túneis rápidos da Cloudflare funcionam com `cloudflared`. O Ngrok exige uma conta ngrok e um authtoken do painel do ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Início rápido
|
||||||
|
|
||||||
1. Inicie o OpenChamber:
|
1. Inicie o OpenChamber:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ Um túnel é um link público para o seu OpenChamber, para que você possa acess
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Inicie um túnel:
|
2. Inicie um túnel da Cloudflare:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Ou inicie um túnel do Ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. Verifique o status:
|
3. Verifique o status:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ Quando o túnel estiver no ar, o `status` mostra uma URL pública. Abra-a, ou es
|
|||||||
|
|
||||||
Por padrão, o OpenChamber imprime um QR code em sessões TTY interativas. Use `--qr` para forçar a saída do QR, ou `--no-qr` para desativá-la.
|
Por padrão, o OpenChamber imprime um QR code em sessões TTY interativas. Use `--qr` para forçar a saída do QR, ou `--no-qr` para desativá-la.
|
||||||
|
|
||||||
|
## Provedores
|
||||||
|
|
||||||
|
- `cloudflare`: modos quick, managed remote e managed local
|
||||||
|
- `ngrok`: modo quick
|
||||||
|
|
||||||
## Modos gerenciados
|
## Modos gerenciados
|
||||||
|
|
||||||
### Gerenciado remoto
|
### Gerenciado remoto
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## Relacionado
|
## Relacionado
|
||||||
|
|
||||||
- [Segurança](/pt-br/security/) — proteja a interface antes de expô-la
|
- [Segurança](/pt-br/security/) — proteja a interface antes de expô-la
|
||||||
|
- [Túneis no desktop](/pt-br/desktop-tunnels/) — configuração de túnel no app desktop sem iniciar pelo CLI
|
||||||
- [PWA e Acesso Móvel](/pt-br/mobile/) — acesse o OpenChamber pelo celular
|
- [PWA e Acesso Móvel](/pt-br/mobile/) — acesse o OpenChamber pelo celular
|
||||||
- [Solução de problemas](/pt-br/troubleshooting/) — se o link do túnel não funcionar
|
- [Solução de problemas](/pt-br/troubleshooting/) — se o link do túnel não funcionar
|
||||||
|
|||||||
@@ -7,7 +7,22 @@ description: Expose OpenChamber safely for remote and mobile access.
|
|||||||
|
|
||||||
A tunnel is a public link to your OpenChamber, so you can reach it from your phone or another network. Use `openchamber tunnel` to create one for a running instance.
|
A tunnel is a public link to your OpenChamber, so you can reach it from your phone or another network. Use `openchamber tunnel` to create one for a running instance.
|
||||||
|
|
||||||
## Quick start (Cloudflare quick mode)
|
## Prerequisites
|
||||||
|
|
||||||
|
OpenChamber starts the tunnel provider CLI on your machine. Install the provider you want to use first:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare quick tunnels can run with `cloudflared`. Ngrok requires an ngrok account and an authtoken from the ngrok dashboard:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
1. Start OpenChamber:
|
1. Start OpenChamber:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ A tunnel is a public link to your OpenChamber, so you can reach it from your pho
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Start a tunnel:
|
2. Start a Cloudflare tunnel:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or start an Ngrok tunnel:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. Check status:
|
3. Check status:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ When the tunnel is up, `status` shows a public URL. Open it, or scan the QR code
|
|||||||
|
|
||||||
By default, OpenChamber prints a QR code in interactive TTY sessions. Use `--qr` to force QR output, or `--no-qr` to disable it.
|
By default, OpenChamber prints a QR code in interactive TTY sessions. Use `--qr` to force QR output, or `--no-qr` to disable it.
|
||||||
|
|
||||||
|
## Providers
|
||||||
|
|
||||||
|
- `cloudflare`: quick, managed remote, and managed local modes
|
||||||
|
- `ngrok`: quick mode
|
||||||
|
|
||||||
## Managed modes
|
## Managed modes
|
||||||
|
|
||||||
### Managed remote
|
### Managed remote
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## Related
|
## Related
|
||||||
|
|
||||||
- [Security](/security/) — protect the UI before exposing it
|
- [Security](/security/) — protect the UI before exposing it
|
||||||
|
- [Desktop Tunnels](/desktop-tunnels/) — desktop app tunnel setup without CLI startup
|
||||||
- [PWA & Mobile](/mobile/) — reach OpenChamber from your phone
|
- [PWA & Mobile](/mobile/) — reach OpenChamber from your phone
|
||||||
- [Troubleshooting](/troubleshooting/) — if the tunnel link doesn't work
|
- [Troubleshooting](/troubleshooting/) — if the tunnel link doesn't work
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: Десктопні тунелі
|
||||||
|
description: Створюйте Cloudflare або Ngrok тунелі з десктопного застосунку.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Десктопні тунелі
|
||||||
|
|
||||||
|
Десктопний застосунок може створити публічний тунель через **Settings → OpenChamber → Tunnel**. Для цього не потрібно запускати OpenChamber через CLI.
|
||||||
|
|
||||||
|
## Встановіть провайдера
|
||||||
|
|
||||||
|
OpenChamber запускає CLI провайдера на вашій машині. Спочатку встановіть провайдера, який хочете використовувати:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare використовує `cloudflared`. Для Ngrok потрібен акаунт ngrok і authtoken з dashboard ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Запуск із застосунку
|
||||||
|
|
||||||
|
1. Відкрийте **Settings → OpenChamber → Tunnel**.
|
||||||
|
2. Виберіть **Cloudflare** або **Ngrok**.
|
||||||
|
3. Запустіть quick tunnel.
|
||||||
|
4. Відскануйте згенерований QR-код з телефона.
|
||||||
|
|
||||||
|
Ngrok зараз підтримує quick tunnels. Cloudflare підтримує quick tunnels і керовані режими Cloudflare.
|
||||||
|
|
||||||
|
## Захист доступу
|
||||||
|
|
||||||
|
Навіть якщо URL провайдера публічний, OpenChamber все одно захищає доступ власним connect token. Згенероване посилання містить одноразовий token, має TTL, а старі невикористані посилання відкликаються, коли ви генеруєте нове або зупиняєте/перезапускаєте тунель.
|
||||||
|
|
||||||
|
## Пов'язане
|
||||||
|
|
||||||
|
- [Тунелі](/uk/tunnels/) — використання тунелів через CLI і керовані режими Cloudflare
|
||||||
|
- [PWA та мобільний доступ](/uk/mobile/) — відкривайте OpenChamber з телефона
|
||||||
@@ -7,7 +7,22 @@ description: Безпечно відкрийте OpenChamber для віддал
|
|||||||
|
|
||||||
Тунель — це публічне посилання на ваш OpenChamber, щоб дістатися до нього з телефона чи з іншої мережі. Скористайтеся `openchamber tunnel`, щоб створити його для запущеного інстансу.
|
Тунель — це публічне посилання на ваш OpenChamber, щоб дістатися до нього з телефона чи з іншої мережі. Скористайтеся `openchamber tunnel`, щоб створити його для запущеного інстансу.
|
||||||
|
|
||||||
## Швидкий старт (швидкий режим Cloudflare)
|
## Передумови
|
||||||
|
|
||||||
|
OpenChamber запускає CLI провайдера тунелю на вашій машині. Спочатку встановіть провайдера, який хочете використовувати:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Швидкі тунелі Cloudflare працюють через `cloudflared`. Для Ngrok потрібен акаунт ngrok і authtoken з dashboard ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Швидкий старт
|
||||||
|
|
||||||
1. Запустіть OpenChamber:
|
1. Запустіть OpenChamber:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ description: Безпечно відкрийте OpenChamber для віддал
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Запустіть тунель:
|
2. Запустіть тунель Cloudflare:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Або запустіть тунель Ngrok:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. Перевірте статус:
|
3. Перевірте статус:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ openchamber tunnel status
|
|||||||
|
|
||||||
Типово OpenChamber виводить QR-код в інтерактивних TTY-сесіях. Використовуйте `--qr`, щоб примусово вивести QR, або `--no-qr`, щоб вимкнути його.
|
Типово OpenChamber виводить QR-код в інтерактивних TTY-сесіях. Використовуйте `--qr`, щоб примусово вивести QR, або `--no-qr`, щоб вимкнути його.
|
||||||
|
|
||||||
|
## Провайдери
|
||||||
|
|
||||||
|
- `cloudflare`: quick, managed remote і managed local режими
|
||||||
|
- `ngrok`: quick режим
|
||||||
|
|
||||||
## Керовані режими
|
## Керовані режими
|
||||||
|
|
||||||
### Кероване віддалене
|
### Кероване віддалене
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## Пов'язане
|
## Пов'язане
|
||||||
|
|
||||||
- [Безпека](/uk/security/) — захистіть інтерфейс перед відкриттям доступу
|
- [Безпека](/uk/security/) — захистіть інтерфейс перед відкриттям доступу
|
||||||
|
- [Десктопні тунелі](/uk/desktop-tunnels/) — налаштування тунелю в десктопному застосунку без CLI-старту
|
||||||
- [PWA та мобільний доступ](/uk/mobile/) — відкривайте OpenChamber з телефона
|
- [PWA та мобільний доступ](/uk/mobile/) — відкривайте OpenChamber з телефона
|
||||||
- [Усунення несправностей](/uk/troubleshooting/) — якщо посилання тунелю не працює
|
- [Усунення несправностей](/uk/troubleshooting/) — якщо посилання тунелю не працює
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: 桌面端隧道
|
||||||
|
description: 从桌面应用创建 Cloudflare 或 Ngrok 隧道。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 桌面端隧道
|
||||||
|
|
||||||
|
桌面应用可以在 **Settings → OpenChamber → Tunnel** 中创建公开隧道。这个流程不需要你从 CLI 启动 OpenChamber。
|
||||||
|
|
||||||
|
## 安装提供商
|
||||||
|
|
||||||
|
OpenChamber 会在你的机器上启动提供商 CLI。请先安装你想使用的提供商:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare 使用 `cloudflared`。Ngrok 需要 ngrok 账号,以及从 ngrok dashboard 获取的 authtoken:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 从应用启动
|
||||||
|
|
||||||
|
1. 打开 **Settings → OpenChamber → Tunnel**。
|
||||||
|
2. 选择 **Cloudflare** 或 **Ngrok**。
|
||||||
|
3. 启动 quick tunnel。
|
||||||
|
4. 用手机扫描生成的二维码。
|
||||||
|
|
||||||
|
Ngrok 目前支持 quick tunnels。Cloudflare 支持 quick tunnels 和 Cloudflare 托管模式。
|
||||||
|
|
||||||
|
## 访问保护
|
||||||
|
|
||||||
|
即使提供商 URL 本身是公开的,OpenChamber 仍会用自己的 connect token 保护访问。生成的连接链接包含一次性 token,有 TTL;当你生成新链接或停止/重启隧道时,旧的未使用链接会被撤销。
|
||||||
|
|
||||||
|
## 相关内容
|
||||||
|
|
||||||
|
- [隧道](/zh-cn/tunnels/) — CLI 隧道用法和 Cloudflare 托管模式
|
||||||
|
- [PWA 与移动访问](/zh-cn/mobile/) — 从手机访问 OpenChamber
|
||||||
@@ -7,7 +7,22 @@ description: 安全地将 OpenChamber 开放给远程和移动访问。
|
|||||||
|
|
||||||
隧道是指向你的 OpenChamber 的一个公开链接,让你可以从手机或另一个网络访问它。使用 `openchamber tunnel` 为正在运行的实例创建一个隧道。
|
隧道是指向你的 OpenChamber 的一个公开链接,让你可以从手机或另一个网络访问它。使用 `openchamber tunnel` 为正在运行的实例创建一个隧道。
|
||||||
|
|
||||||
## 快速开始(Cloudflare 快速模式)
|
## 前置要求
|
||||||
|
|
||||||
|
OpenChamber 会在你的机器上启动隧道提供商的 CLI。请先安装你想使用的提供商:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install cloudflared
|
||||||
|
brew install ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Cloudflare 快速隧道可以通过 `cloudflared` 运行。Ngrok 需要 ngrok 账号,以及从 ngrok dashboard 获取的 authtoken:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ngrok config add-authtoken <your-ngrok-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
1. 启动 OpenChamber:
|
1. 启动 OpenChamber:
|
||||||
|
|
||||||
@@ -15,12 +30,18 @@ description: 安全地将 OpenChamber 开放给远程和移动访问。
|
|||||||
openchamber
|
openchamber
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 启动隧道:
|
2. 启动 Cloudflare 隧道:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openchamber tunnel start --provider cloudflare --mode quick
|
openchamber tunnel start --provider cloudflare --mode quick
|
||||||
```
|
```
|
||||||
|
|
||||||
|
或启动 Ngrok 隧道:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openchamber tunnel start --provider ngrok --mode quick
|
||||||
|
```
|
||||||
|
|
||||||
3. 查看状态:
|
3. 查看状态:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -31,6 +52,11 @@ openchamber tunnel status
|
|||||||
|
|
||||||
默认情况下,OpenChamber 会在交互式 TTY 会话中输出二维码。使用 `--qr` 强制输出二维码,或使用 `--no-qr` 将其禁用。
|
默认情况下,OpenChamber 会在交互式 TTY 会话中输出二维码。使用 `--qr` 强制输出二维码,或使用 `--no-qr` 将其禁用。
|
||||||
|
|
||||||
|
## 提供商
|
||||||
|
|
||||||
|
- `cloudflare`:quick、managed remote 和 managed local 模式
|
||||||
|
- `ngrok`:quick 模式
|
||||||
|
|
||||||
## 托管模式
|
## 托管模式
|
||||||
|
|
||||||
### 托管远程
|
### 托管远程
|
||||||
@@ -68,7 +94,9 @@ openchamber tunnel start --profile prod-main
|
|||||||
```bash
|
```bash
|
||||||
openchamber tunnel providers
|
openchamber tunnel providers
|
||||||
openchamber tunnel ready --provider cloudflare
|
openchamber tunnel ready --provider cloudflare
|
||||||
|
openchamber tunnel ready --provider ngrok
|
||||||
openchamber tunnel doctor --provider cloudflare
|
openchamber tunnel doctor --provider cloudflare
|
||||||
|
openchamber tunnel doctor --provider ngrok
|
||||||
openchamber tunnel stop --port 3000
|
openchamber tunnel stop --port 3000
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,5 +109,6 @@ openchamber tunnel stop --port 3000
|
|||||||
## 相关内容
|
## 相关内容
|
||||||
|
|
||||||
- [安全](/zh-cn/security/) — 在开放访问前保护 UI
|
- [安全](/zh-cn/security/) — 在开放访问前保护 UI
|
||||||
|
- [桌面端隧道](/zh-cn/desktop-tunnels/) — 无需从 CLI 启动即可在桌面应用中设置隧道
|
||||||
- [PWA 与移动访问](/zh-cn/mobile/) — 从手机访问 OpenChamber
|
- [PWA 与移动访问](/zh-cn/mobile/) — 从手机访问 OpenChamber
|
||||||
- [问题排查](/zh-cn/troubleshooting/) — 如果隧道链接无法使用
|
- [问题排查](/zh-cn/troubleshooting/) — 如果隧道链接无法使用
|
||||||
|
|||||||
@@ -444,6 +444,18 @@
|
|||||||
"pl": "Przeglądarka na pulpicie"
|
"pl": "Przeglądarka na pulpicie"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Desktop Tunnels",
|
||||||
|
"link": "/desktop-tunnels/",
|
||||||
|
"translations": {
|
||||||
|
"uk": "Десктопні тунелі",
|
||||||
|
"zh-CN": "桌面端隧道",
|
||||||
|
"es": "Túneles de escritorio",
|
||||||
|
"pt-BR": "Túneis no desktop",
|
||||||
|
"ko": "데스크톱 터널",
|
||||||
|
"pl": "Tunele w aplikacji desktopowej"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Updates",
|
"label": "Updates",
|
||||||
"link": "/updates/",
|
"link": "/updates/",
|
||||||
|
|||||||
@@ -149,16 +149,20 @@ const getProviderLabel = (provider: string): string => {
|
|||||||
if (provider === 'cloudflare') {
|
if (provider === 'cloudflare') {
|
||||||
return 'Cloudflare';
|
return 'Cloudflare';
|
||||||
}
|
}
|
||||||
|
if (provider === 'ngrok') {
|
||||||
|
return 'Ngrok';
|
||||||
|
}
|
||||||
return provider;
|
return provider;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProviderOptionLabel: React.FC<{ provider: string }> = ({ provider }) => {
|
const ProviderOptionLabel: React.FC<{ provider: string }> = ({ provider }) => {
|
||||||
const label = getProviderLabel(provider);
|
const label = getProviderLabel(provider);
|
||||||
const isCloudflare = provider === 'cloudflare';
|
const isCloudflare = provider === 'cloudflare';
|
||||||
|
const isNgrok = provider === 'ngrok';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<Icon name="cloud" className={cn('size-4 shrink-0', isCloudflare ? 'text-[var(--status-warning)]' : 'text-muted-foreground')} />
|
<Icon name="cloud" className={cn('size-4 shrink-0', isCloudflare || isNgrok ? 'text-[var(--status-warning)]' : 'text-muted-foreground')} />
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -182,6 +186,11 @@ const ttlOptionValue = (options: TtlOption[], ttlMs: number | null, fallback: st
|
|||||||
return matched?.value || fallback;
|
return matched?.value || fallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ttlOptionLabel = (options: TtlOption[], ttlMs: number | null, fallback: string) => {
|
||||||
|
const value = ttlOptionValue(options, ttlMs, fallback);
|
||||||
|
return options.find((entry) => entry.value === value)?.label || value;
|
||||||
|
};
|
||||||
|
|
||||||
const formatRemaining = (remainingMs: number): string => {
|
const formatRemaining = (remainingMs: number): string => {
|
||||||
const safeMs = Math.max(0, remainingMs);
|
const safeMs = Math.max(0, remainingMs);
|
||||||
const totalSeconds = Math.floor(safeMs / 1000);
|
const totalSeconds = Math.floor(safeMs / 1000);
|
||||||
@@ -361,6 +370,23 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [localPort]);
|
}, [localPort]);
|
||||||
|
const selectedProviderCapability = React.useMemo(() => {
|
||||||
|
return providerCapabilities.find((capability) => capability.provider === tunnelProvider) ?? null;
|
||||||
|
}, [providerCapabilities, tunnelProvider]);
|
||||||
|
const tunnelModeOptions = React.useMemo(() => {
|
||||||
|
const supportedModes = new Set(
|
||||||
|
selectedProviderCapability?.modes
|
||||||
|
?.map((mode) => mode.key)
|
||||||
|
.filter((mode): mode is TunnelMode => mode === 'quick' || mode === 'managed-remote' || mode === 'managed-local')
|
||||||
|
);
|
||||||
|
if (supportedModes.size === 0) {
|
||||||
|
return TUNNEL_MODE_OPTIONS;
|
||||||
|
}
|
||||||
|
return TUNNEL_MODE_OPTIONS.filter((option) => supportedModes.has(option.value));
|
||||||
|
}, [selectedProviderCapability]);
|
||||||
|
const installCommand = tunnelProvider === 'ngrok'
|
||||||
|
? 'brew install ngrok'
|
||||||
|
: 'brew install cloudflared';
|
||||||
const openExternal = React.useCallback(async (url: string) => {
|
const openExternal = React.useCallback(async (url: string) => {
|
||||||
await openExternalUrl(url);
|
await openExternalUrl(url);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -691,8 +717,12 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
const handleProviderChange = React.useCallback(async (provider: string) => {
|
const handleProviderChange = React.useCallback(async (provider: string) => {
|
||||||
setManagedRemoteValidationError(null);
|
setManagedRemoteValidationError(null);
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
await saveTunnelSettings({ tunnelProvider: provider });
|
const capability = providerCapabilities.find((entry) => entry.provider === provider);
|
||||||
}, [saveTunnelSettings]);
|
const defaultMode = capability?.modes?.some((mode) => mode.key === tunnelMode)
|
||||||
|
? tunnelMode
|
||||||
|
: toUiTunnelMode(capability?.modes?.[0]?.key);
|
||||||
|
await saveTunnelSettings({ tunnelProvider: provider, tunnelMode: defaultMode });
|
||||||
|
}, [providerCapabilities, saveTunnelSettings, tunnelMode]);
|
||||||
|
|
||||||
const handleBrowseManagedLocalConfig = React.useCallback(async () => {
|
const handleBrowseManagedLocalConfig = React.useCallback(async () => {
|
||||||
const result = await requestFileAccess({
|
const result = await requestFileAccess({
|
||||||
@@ -1146,17 +1176,17 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
<div className="flex items-start gap-2 rounded-lg border border-[var(--status-warning)]/30 bg-[var(--status-warning)]/5 p-3">
|
<div className="flex items-start gap-2 rounded-lg border border-[var(--status-warning)]/30 bg-[var(--status-warning)]/5 p-3">
|
||||||
<Icon name="error-warning" className="mt-0.5 size-4 shrink-0 text-[var(--status-warning)]" />
|
<Icon name="error-warning" className="mt-0.5 size-4 shrink-0 text-[var(--status-warning)]" />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="typography-meta font-medium text-foreground">{t('settings.openchamber.tunnel.notAvailable.cloudflaredNotFound')}</p>
|
<p className="typography-meta font-medium text-foreground">{getProviderLabel(tunnelProvider)} tunnel dependency was not found.</p>
|
||||||
<p className="typography-meta text-muted-foreground/70">{t('settings.openchamber.tunnel.notAvailable.installHint')}</p>
|
<p className="typography-meta text-muted-foreground/70">{t('settings.openchamber.tunnel.notAvailable.installHint')}</p>
|
||||||
<code className="typography-code block rounded bg-muted/50 px-2 py-1 text-xs text-foreground">
|
<code className="typography-code block rounded bg-muted/50 px-2 py-1 text-xs text-foreground">
|
||||||
brew install cloudflared
|
{installCommand}
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{state !== 'not-available' && (
|
{(
|
||||||
<section className="space-y-4 px-2 pb-2 pt-0">
|
<section className="space-y-4 px-2 pb-2 pt-0">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
@@ -1193,7 +1223,7 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<p className="typography-ui-label text-foreground">{t('settings.openchamber.tunnel.field.tunnelType')}</p>
|
<p className="typography-ui-label text-foreground">{t('settings.openchamber.tunnel.field.tunnelType')}</p>
|
||||||
<div className="flex flex-wrap items-center gap-1">
|
<div className="flex flex-wrap items-center gap-1">
|
||||||
{TUNNEL_MODE_OPTIONS.map((option) => (
|
{tunnelModeOptions.map((option) => (
|
||||||
<Tooltip key={option.value}>
|
<Tooltip key={option.value}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -1229,7 +1259,9 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
disabled={isSavingTtl || isSavingMode || state === 'starting' || state === 'stopping'}
|
disabled={isSavingTtl || isSavingMode || state === 'starting' || state === 'stopping'}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="max-w-[11rem] min-w-0">
|
<SelectTrigger className="max-w-[11rem] min-w-0">
|
||||||
<SelectValue className="truncate" />
|
<SelectValue className="truncate">
|
||||||
|
{ttlOptionLabel(BOOTSTRAP_TTL_OPTIONS, bootstrapTtlMs, '1800000')}
|
||||||
|
</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{BOOTSTRAP_TTL_OPTIONS.map((option) => (
|
{BOOTSTRAP_TTL_OPTIONS.map((option) => (
|
||||||
@@ -1249,7 +1281,9 @@ export const TunnelSettings: React.FC = () => {
|
|||||||
disabled={isSavingTtl || isSavingMode || state === 'starting' || state === 'stopping'}
|
disabled={isSavingTtl || isSavingMode || state === 'starting' || state === 'stopping'}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="max-w-[11rem] min-w-0">
|
<SelectTrigger className="max-w-[11rem] min-w-0">
|
||||||
<SelectValue className="truncate" />
|
<SelectValue className="truncate">
|
||||||
|
{ttlOptionLabel(SESSION_TTL_OPTIONS, sessionTtlMs, '28800000')}
|
||||||
|
</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{SESSION_TTL_OPTIONS.map((option) => (
|
{SESSION_TTL_OPTIONS.map((option) => (
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { createTunnelAuth } from './lib/opencode/tunnel-auth.js';
|
|||||||
import { createManagedTunnelConfigRuntime } from './lib/tunnels/managed-config.js';
|
import { createManagedTunnelConfigRuntime } from './lib/tunnels/managed-config.js';
|
||||||
import { createTunnelProviderRegistry } from './lib/tunnels/registry.js';
|
import { createTunnelProviderRegistry } from './lib/tunnels/registry.js';
|
||||||
import { createCloudflareTunnelProvider } from './lib/tunnels/providers/cloudflare.js';
|
import { createCloudflareTunnelProvider } from './lib/tunnels/providers/cloudflare.js';
|
||||||
|
import { createNgrokTunnelProvider } from './lib/tunnels/providers/ngrok.js';
|
||||||
import { createRequestSecurityRuntime } from './lib/security/request-security.js';
|
import { createRequestSecurityRuntime } from './lib/security/request-security.js';
|
||||||
import {
|
import {
|
||||||
TUNNEL_MODE_MANAGED_LOCAL,
|
TUNNEL_MODE_MANAGED_LOCAL,
|
||||||
@@ -449,6 +450,7 @@ let activeTunnelController = null;
|
|||||||
let globalWatcherStartPromise = null;
|
let globalWatcherStartPromise = null;
|
||||||
const tunnelProviderRegistry = createTunnelProviderRegistry([
|
const tunnelProviderRegistry = createTunnelProviderRegistry([
|
||||||
createCloudflareTunnelProvider(),
|
createCloudflareTunnelProvider(),
|
||||||
|
createNgrokTunnelProvider(),
|
||||||
]);
|
]);
|
||||||
tunnelProviderRegistry.seal();
|
tunnelProviderRegistry.seal();
|
||||||
const tunnelAuthController = createTunnelAuth();
|
const tunnelAuthController = createTunnelAuth();
|
||||||
|
|||||||
@@ -637,14 +637,12 @@ export async function startCloudflareTunnel({ originUrl, port }) {
|
|||||||
|
|
||||||
export function printTunnelWarning() {
|
export function printTunnelWarning() {
|
||||||
console.log(`
|
console.log(`
|
||||||
⚠️ Cloudflare Quick Tunnel Limitations:
|
⚠️ Quick Tunnel Limitations:
|
||||||
|
|
||||||
• Maximum 200 concurrent requests
|
• Provider limits may apply
|
||||||
• Server-Sent Events (SSE) are NOT supported
|
|
||||||
• URLs are temporary and will expire when the tunnel stops
|
• URLs are temporary and will expire when the tunnel stops
|
||||||
• Password protection is required for tunnel access
|
• Password protection is required for tunnel access
|
||||||
|
|
||||||
For production use, set up a managed remote Cloudflare Tunnel:
|
For production use, set up a persistent provider tunnel or static domain.
|
||||||
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/
|
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,209 @@
|
|||||||
|
import { spawn, spawnSync } from 'child_process';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const DEFAULT_STARTUP_TIMEOUT_MS = 30000;
|
||||||
|
const NGROK_API_URL = 'http://127.0.0.1:4040/api/tunnels';
|
||||||
|
const NGROK_INSTALL_HELP = 'brew install ngrok';
|
||||||
|
const NGROK_AUTHTOKEN_HELP = 'Run: ngrok config add-authtoken <your-ngrok-token>';
|
||||||
|
|
||||||
|
async function searchPathFor(command) {
|
||||||
|
const pathValue = process.env.PATH || '';
|
||||||
|
const segments = pathValue.split(path.delimiter).filter(Boolean);
|
||||||
|
const WINDOWS_EXTENSIONS = process.platform === 'win32'
|
||||||
|
? (process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
|
||||||
|
.split(';')
|
||||||
|
.map((ext) => ext.trim().toLowerCase())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((ext) => (ext.startsWith('.') ? ext : `.${ext}`))
|
||||||
|
: [''];
|
||||||
|
|
||||||
|
for (const dir of segments) {
|
||||||
|
for (const ext of WINDOWS_EXTENSIONS) {
|
||||||
|
const fileName = process.platform === 'win32' ? `${command}${ext}` : command;
|
||||||
|
const candidate = path.join(dir, fileName);
|
||||||
|
try {
|
||||||
|
const stats = fs.statSync(candidate);
|
||||||
|
if (!stats.isFile()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (process.platform !== 'win32') {
|
||||||
|
try {
|
||||||
|
fs.accessSync(candidate, fs.constants.X_OK);
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkNgrokAvailable() {
|
||||||
|
const ngrokPath = await searchPathFor('ngrok');
|
||||||
|
if (ngrokPath) {
|
||||||
|
try {
|
||||||
|
const result = spawnSync(ngrokPath, ['version'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
windowsHide: true,
|
||||||
|
});
|
||||||
|
if (result.status === 0) {
|
||||||
|
return { available: true, path: ngrokPath, version: result.stdout.trim() || result.stderr.trim() };
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore and report unavailable below.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { available: false, path: null, version: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkNgrokAuthtokenConfigured(ngrokPath = null) {
|
||||||
|
if (typeof process.env.NGROK_AUTHTOKEN === 'string' && process.env.NGROK_AUTHTOKEN.trim().length > 0) {
|
||||||
|
return { configured: true, detail: 'NGROK_AUTHTOKEN is set.' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedPath = ngrokPath || await searchPathFor('ngrok');
|
||||||
|
if (!resolvedPath) {
|
||||||
|
return { configured: false, detail: `ngrok is not installed. Install it with: ${NGROK_INSTALL_HELP}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = spawnSync(resolvedPath, ['config', 'check'], {
|
||||||
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
windowsHide: true,
|
||||||
|
});
|
||||||
|
const output = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
||||||
|
if (result.status === 0) {
|
||||||
|
return { configured: true, detail: output || 'ngrok config is valid.' };
|
||||||
|
}
|
||||||
|
return { configured: false, detail: output || NGROK_AUTHTOKEN_HELP };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
configured: false,
|
||||||
|
detail: error instanceof Error ? error.message : String(error),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkNgrokApiReachability({ fetchImpl = globalThis.fetch, timeoutMs = 5000 } = {}) {
|
||||||
|
if (typeof fetchImpl !== 'function') {
|
||||||
|
return { reachable: false, status: null, error: 'Fetch API is unavailable in this runtime.' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
||||||
|
try {
|
||||||
|
const response = await fetchImpl('https://api.ngrok.com/', {
|
||||||
|
method: 'GET',
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
return { reachable: true, status: response.status, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
reachable: false,
|
||||||
|
status: null,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const spawnNgrok = (args, resolvedBinaryPath = 'ngrok') => spawn(resolvedBinaryPath, args, {
|
||||||
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
|
windowsHide: true,
|
||||||
|
env: process.env,
|
||||||
|
killSignal: 'SIGINT',
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchNgrokPublicUrl(fetchImpl = globalThis.fetch) {
|
||||||
|
if (typeof fetchImpl !== 'function') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetchImpl(NGROK_API_URL, { method: 'GET' });
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const payload = await response.json();
|
||||||
|
const tunnels = Array.isArray(payload?.tunnels) ? payload.tunnels : [];
|
||||||
|
const httpsTunnel = tunnels.find((entry) => entry?.proto === 'https' && typeof entry?.public_url === 'string');
|
||||||
|
const fallbackTunnel = tunnels.find((entry) => typeof entry?.public_url === 'string');
|
||||||
|
return httpsTunnel?.public_url || fallbackTunnel?.public_url || null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startNgrokQuickTunnel({ port }) {
|
||||||
|
const ngrokCheck = await checkNgrokAvailable();
|
||||||
|
if (!ngrokCheck.available) {
|
||||||
|
throw new Error(`ngrok is not installed. Install it with: ${NGROK_INSTALL_HELP}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const authtokenCheck = await checkNgrokAuthtokenConfigured(ngrokCheck.path);
|
||||||
|
if (!authtokenCheck.configured) {
|
||||||
|
throw new Error(`ngrok authtoken is not configured. ${NGROK_AUTHTOKEN_HELP}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Number.isFinite(port)) {
|
||||||
|
throw new Error('A local port is required to start an ngrok tunnel');
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = spawnNgrok(['http', String(port)], ngrokCheck.path);
|
||||||
|
let publicUrl = null;
|
||||||
|
|
||||||
|
child.stdout.on('data', () => {
|
||||||
|
// Keep stream drained; ngrok exposes the URL via its local API.
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on('data', (chunk) => {
|
||||||
|
process.stderr.write(chunk.toString('utf8'));
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (error) => {
|
||||||
|
console.error(`Ngrok error: ${error.message}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
clearInterval(checkReady);
|
||||||
|
try { child.kill('SIGINT'); } catch { /* ignore */ }
|
||||||
|
reject(new Error('Ngrok tunnel URL not received within 30 seconds'));
|
||||||
|
}, DEFAULT_STARTUP_TIMEOUT_MS);
|
||||||
|
|
||||||
|
const checkReady = setInterval(async () => {
|
||||||
|
publicUrl = await fetchNgrokPublicUrl();
|
||||||
|
if (publicUrl) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
clearInterval(checkReady);
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
child.once('exit', (code) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
clearInterval(checkReady);
|
||||||
|
reject(new Error(`Ngrok exited while starting (code ${code ?? 'unknown'})`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
mode: 'quick',
|
||||||
|
stop: () => {
|
||||||
|
try {
|
||||||
|
child.kill('SIGINT');
|
||||||
|
} catch {
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
process: child,
|
||||||
|
getPublicUrl: () => publicUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ This module contains tunnel provider orchestration for OpenChamber, including pr
|
|||||||
- `packages/web/server/lib/tunnels/routes.js`: tunnel API route registration and request orchestration runtime.
|
- `packages/web/server/lib/tunnels/routes.js`: tunnel API route registration and request orchestration runtime.
|
||||||
- `packages/web/server/lib/tunnels/types.js`: tunnel constants, normalization, and shared type helpers.
|
- `packages/web/server/lib/tunnels/types.js`: tunnel constants, normalization, and shared type helpers.
|
||||||
- `packages/web/server/lib/tunnels/providers/cloudflare.js`: Cloudflare tunnel provider implementation.
|
- `packages/web/server/lib/tunnels/providers/cloudflare.js`: Cloudflare tunnel provider implementation.
|
||||||
|
- `packages/web/server/lib/tunnels/providers/ngrok.js`: Ngrok quick tunnel provider implementation.
|
||||||
|
|
||||||
## Public exports (routes.js)
|
## Public exports (routes.js)
|
||||||
- `createTunnelRoutesRuntime(dependencies)`: creates tunnel routes runtime and helpers.
|
- `createTunnelRoutesRuntime(dependencies)`: creates tunnel routes runtime and helpers.
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import {
|
||||||
|
checkNgrokApiReachability,
|
||||||
|
checkNgrokAuthtokenConfigured,
|
||||||
|
checkNgrokAvailable,
|
||||||
|
startNgrokQuickTunnel,
|
||||||
|
} from '../../ngrok-tunnel.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
||||||
|
TUNNEL_MODE_QUICK,
|
||||||
|
TUNNEL_PROVIDER_NGROK,
|
||||||
|
TunnelServiceError,
|
||||||
|
} from '../types.js';
|
||||||
|
|
||||||
|
export const ngrokTunnelProviderCapabilities = {
|
||||||
|
provider: TUNNEL_PROVIDER_NGROK,
|
||||||
|
defaults: {
|
||||||
|
mode: TUNNEL_MODE_QUICK,
|
||||||
|
optionDefaults: {},
|
||||||
|
},
|
||||||
|
modes: [
|
||||||
|
{
|
||||||
|
key: TUNNEL_MODE_QUICK,
|
||||||
|
label: 'Quick Tunnel',
|
||||||
|
intent: TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
||||||
|
requires: [],
|
||||||
|
supports: ['sessionTTL'],
|
||||||
|
stability: 'beta',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createNgrokTunnelProvider() {
|
||||||
|
return {
|
||||||
|
id: TUNNEL_PROVIDER_NGROK,
|
||||||
|
capabilities: ngrokTunnelProviderCapabilities,
|
||||||
|
checkAvailability: async () => {
|
||||||
|
const result = await checkNgrokAvailable();
|
||||||
|
if (result.available) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
message: 'ngrok is not installed. Install it with: brew install ngrok',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
diagnose: async () => {
|
||||||
|
const dependency = await checkNgrokAvailable();
|
||||||
|
const authtoken = await checkNgrokAuthtokenConfigured(dependency.path);
|
||||||
|
const network = await checkNgrokApiReachability();
|
||||||
|
const startupReady = dependency.available && authtoken.configured && network.reachable;
|
||||||
|
const providerChecks = [
|
||||||
|
{
|
||||||
|
id: 'dependency',
|
||||||
|
label: 'ngrok installed',
|
||||||
|
status: dependency.available ? 'pass' : 'fail',
|
||||||
|
detail: dependency.available
|
||||||
|
? (dependency.version || dependency.path || 'ngrok available')
|
||||||
|
: 'ngrok is not installed. Install it with: brew install ngrok',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'authtoken',
|
||||||
|
label: 'ngrok authtoken configured',
|
||||||
|
status: authtoken.configured ? 'pass' : 'fail',
|
||||||
|
detail: authtoken.configured
|
||||||
|
? authtoken.detail
|
||||||
|
: (authtoken.detail || 'Run: ngrok config add-authtoken <your-ngrok-token>'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'network',
|
||||||
|
label: 'ngrok API reachable',
|
||||||
|
status: network.reachable ? 'pass' : 'fail',
|
||||||
|
detail: network.reachable
|
||||||
|
? (network.status ? `HTTP ${network.status}` : 'Reachable')
|
||||||
|
: (network.error || 'Could not reach api.ngrok.com'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
providerChecks,
|
||||||
|
modes: [
|
||||||
|
{
|
||||||
|
mode: TUNNEL_MODE_QUICK,
|
||||||
|
checks: [
|
||||||
|
{
|
||||||
|
id: 'startup_readiness',
|
||||||
|
label: 'Provider startup readiness',
|
||||||
|
status: startupReady ? 'pass' : 'fail',
|
||||||
|
detail: startupReady
|
||||||
|
? 'Provider dependency, auth, and network checks passed.'
|
||||||
|
: 'Resolve provider checks before starting tunnels.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
summary: {
|
||||||
|
ready: startupReady,
|
||||||
|
failures: startupReady ? 0 : 1,
|
||||||
|
warnings: 0,
|
||||||
|
},
|
||||||
|
ready: startupReady,
|
||||||
|
blockers: startupReady ? [] : ['Resolve provider checks before starting tunnels.'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
start: async (request, context = {}) => {
|
||||||
|
if (request.mode !== TUNNEL_MODE_QUICK) {
|
||||||
|
throw new TunnelServiceError('mode_unsupported', `Ngrok only supports '${TUNNEL_MODE_QUICK}' mode right now`);
|
||||||
|
}
|
||||||
|
return startNgrokQuickTunnel({ port: context.activePort });
|
||||||
|
},
|
||||||
|
stop: (controller) => {
|
||||||
|
controller?.stop?.();
|
||||||
|
},
|
||||||
|
resolvePublicUrl: (controller) => controller?.getPublicUrl?.() ?? null,
|
||||||
|
getMetadata: () => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import os from 'os';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export const TUNNEL_PROVIDER_CLOUDFLARE = 'cloudflare';
|
export const TUNNEL_PROVIDER_CLOUDFLARE = 'cloudflare';
|
||||||
|
export const TUNNEL_PROVIDER_NGROK = 'ngrok';
|
||||||
|
|
||||||
export const TUNNEL_MODE_QUICK = 'quick';
|
export const TUNNEL_MODE_QUICK = 'quick';
|
||||||
export const TUNNEL_MODE_MANAGED_REMOTE = 'managed-remote';
|
export const TUNNEL_MODE_MANAGED_REMOTE = 'managed-remote';
|
||||||
@@ -34,6 +35,7 @@ export class TunnelServiceError extends Error {
|
|||||||
|
|
||||||
const SUPPORTED_TUNNEL_PROVIDERS = new Set([
|
const SUPPORTED_TUNNEL_PROVIDERS = new Set([
|
||||||
TUNNEL_PROVIDER_CLOUDFLARE,
|
TUNNEL_PROVIDER_CLOUDFLARE,
|
||||||
|
TUNNEL_PROVIDER_NGROK,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function normalizeTunnelProvider(value) {
|
export function normalizeTunnelProvider(value) {
|
||||||
|
|||||||
Reference in New Issue
Block a user