Skip to content

Commit 6a4f2cd

Browse files
authored
Merge pull request #97 from OpenMined/feat/auto-fetch-tunnel-credentials
feat: auto-fetch ngrok tunnel credentials from SyftHub
2 parents 798adce + ef7aca0 commit 6a4f2cd

15 files changed

Lines changed: 229 additions & 165 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""rename ngrok_username to ngrok_domain
2+
3+
Revision ID: a1b2c3d4e5f6
4+
Revises: 616fe141adf9
5+
Create Date: 2026-03-04 12:00:00.000000
6+
7+
"""
8+
9+
from collections.abc import Sequence
10+
11+
import sqlalchemy as sa
12+
import sqlmodel
13+
from alembic import op
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "a1b2c3d4e5f6"
17+
down_revision: str | None = "616fe141adf9"
18+
branch_labels: str | Sequence[str] | None = None
19+
depends_on: str | Sequence[str] | None = None
20+
21+
22+
def upgrade() -> None:
23+
op.add_column(
24+
"settings",
25+
sa.Column("ngrok_domain", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
26+
)
27+
op.drop_column("settings", "ngrok_username")
28+
29+
30+
def downgrade() -> None:
31+
op.add_column(
32+
"settings",
33+
sa.Column("ngrok_username", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
34+
)
35+
op.drop_column("settings", "ngrok_domain")

backend/syft_space/components/settings/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from syft_space.components.settings.handlers import SettingsHandler
55
from syft_space.components.settings.repository import SettingsRepository
66
from syft_space.components.settings.schemas import (
7-
ProxyConfigRequest,
87
ProxyStatusResponse,
98
PublicUrlResponse,
109
UpdatePublicUrlRequest,
@@ -14,7 +13,6 @@
1413
"Settings",
1514
"SettingsHandler",
1615
"SettingsRepository",
17-
"ProxyConfigRequest",
1816
"ProxyStatusResponse",
1917
"PublicUrlResponse",
2018
"UpdatePublicUrlRequest",

backend/syft_space/components/settings/entities.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Settings(SQLModel, table=True):
1818
ngrok_token: str | None = Field(
1919
default=None, description="Ngrok authentication token for proxy tunnel"
2020
)
21-
ngrok_username: str | None = Field(
22-
default=None, description="Username for ngrok subdomain"
21+
ngrok_domain: str | None = Field(
22+
default=None, description="Domain for ngrok tunnel"
2323
)
2424
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))

backend/syft_space/components/settings/handlers.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,14 @@ async def get_proxy_status(self) -> ProxyStatusResponse:
134134
has_token=has_token,
135135
)
136136

137-
async def configure_proxy(self, tenant: Tenant, token: str) -> ProxyStatusResponse:
137+
async def configure_proxy(self, tenant: Tenant) -> ProxyStatusResponse:
138138
"""Configure the ngrok proxy tunnel.
139139
140-
Connects to ngrok with the provided token and persists the configuration.
141-
Also syncs the public URL to the marketplace.
140+
Delegates to ProxyService.connect_with_fallback() which tries stored
141+
credentials first and falls back to fresh ones from SyftHub.
142142
143143
Args:
144144
tenant: Tenant context for marketplace sync
145-
token: Ngrok authentication token
146145
147146
Returns:
148147
Proxy status response with connection status and public URL
@@ -156,7 +155,7 @@ async def configure_proxy(self, tenant: Tenant, token: str) -> ProxyStatusRespon
156155
detail="Ngrok proxy service not configured",
157156
)
158157

159-
# Get default marketplace
158+
# Get default marketplace (needed for URL sync)
160159
marketplace = await self.marketplace_repository.get_default(tenant.id)
161160

162161
if not marketplace:
@@ -166,7 +165,12 @@ async def configure_proxy(self, tenant: Tenant, token: str) -> ProxyStatusRespon
166165
)
167166

168167
try:
169-
public_url = await self.proxy_service.connect(token, marketplace.username)
168+
public_url = await self.proxy_service.connect()
169+
except SyftHubError as e:
170+
raise HTTPException(
171+
status_code=e.status_code,
172+
detail=f"Failed to fetch tunnel credentials: {e.message}",
173+
) from e
170174
except Exception as e:
171175
logger.exception(f"Failed to connect to ngrok: {e}")
172176
raise HTTPException(status_code=400, detail=str(e)) from e

backend/syft_space/components/settings/repository.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ async def update_ngrok_token(self, token: str | None) -> Settings:
4848
settings.updated_at = datetime.now(timezone.utc)
4949
return await self.update(settings)
5050

51-
async def get_ngrok_username(self) -> str | None:
52-
"""Get the stored ngrok username for subdomain."""
51+
async def get_ngrok_domain(self) -> str | None:
52+
"""Get the stored ngrok domain for tunnel."""
5353
settings = await self.get_settings()
54-
return settings.ngrok_username
54+
return settings.ngrok_domain
5555

56-
async def update_ngrok_username(self, username: str | None) -> Settings:
57-
"""Update the ngrok username setting."""
56+
async def update_ngrok_domain(self, domain: str | None) -> Settings:
57+
"""Update the ngrok domain setting."""
5858
settings = await self.get_settings()
59-
settings.ngrok_username = username
59+
settings.ngrok_domain = domain
6060
settings.updated_at = datetime.now(timezone.utc)
6161
return await self.update(settings)

backend/syft_space/components/settings/routes.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from syft_space.components.settings.handlers import SettingsHandler
66
from syft_space.components.settings.schemas import (
7-
ProxyConfigRequest,
87
ProxyStatusResponse,
98
PublicUrlResponse,
109
UpdatePublicUrlRequest,
@@ -58,16 +57,15 @@ async def get_proxy_status(
5857

5958
@router.post("/proxy", response_model=ProxyStatusResponse)
6059
async def configure_proxy(
61-
request: ProxyConfigRequest,
6260
tenant: Tenant = Depends(get_tenant_dependency),
6361
handler: SettingsHandler = Depends(get_handler),
6462
) -> ProxyStatusResponse:
6563
"""Configure the ngrok proxy tunnel.
6664
67-
Connects to ngrok with the provided token and persists the configuration.
68-
The tunnel will automatically reconnect on app restart.
65+
Fetches tunnel credentials from SyftHub, connects to ngrok, and persists
66+
the configuration. The tunnel will automatically reconnect on app restart.
6967
"""
70-
return await handler.configure_proxy(tenant, request.ngrok_token)
68+
return await handler.configure_proxy(tenant)
7169

7270
@router.delete("/proxy", response_model=ProxyStatusResponse)
7371
async def disconnect_proxy(

backend/syft_space/components/settings/schemas.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,6 @@ class Config:
2424
}
2525

2626

27-
class ProxyConfigRequest(BaseModel):
28-
"""Request model for configuring the ngrok proxy."""
29-
30-
ngrok_token: str = Field(
31-
..., min_length=1, description="Ngrok authentication token"
32-
)
33-
34-
class Config:
35-
"""Pydantic config."""
36-
37-
json_schema_extra = {
38-
"example": {
39-
"ngrok_token": "2abc123def456...",
40-
}
41-
}
42-
43-
4427
class ProxyStatusResponse(BaseModel):
4528
"""Response model for proxy status."""
4629

0 commit comments

Comments
 (0)