1from __future__ import annotations
2
3import getpass
4import random
5import string
6
7import click
8from plain.cli import register_cli
9
10from .client import TunnelClient
11
12
13@register_cli("tunnel")
14@click.command()
15@click.argument("destination")
16@click.option(
17 "--subdomain",
18 help="The subdomain to use for the tunnel.",
19 envvar="PLAIN_TUNNEL_SUBDOMAIN",
20)
21@click.option(
22 "--tunnel-host", envvar="PLAIN_TUNNEL_HOST", hidden=True, default="plaintunnel.com"
23)
24@click.option("--debug", "log_level", flag_value="DEBUG", help="Enable debug logging.")
25@click.option(
26 "--quiet", "log_level", flag_value="WARNING", help="Only log warnings and errors."
27)
28def cli(
29 destination: str, subdomain: str | None, tunnel_host: str, log_level: str | None
30) -> None:
31 """Create a public tunnel to local server"""
32 if not destination.startswith("http://") and not destination.startswith("https://"):
33 destination = f"https://{destination}"
34
35 # Strip trailing slashes from the destination URL (maybe even enforce no path at all?)
36 destination = destination.rstrip("/")
37
38 if not log_level:
39 log_level = "INFO"
40
41 if not subdomain:
42 # Generate a subdomain using the system username + 7 random characters
43 random_chars = "".join(random.choices(string.ascii_lowercase, k=7))
44 subdomain = f"{getpass.getuser()}-{random_chars}"
45
46 tunnel = TunnelClient(
47 destination_url=destination,
48 subdomain=subdomain,
49 tunnel_host=tunnel_host,
50 log_level=log_level,
51 )
52 click.secho(f"Tunneling {tunnel.tunnel_http_url} -> {destination}", bold=True)
53 tunnel.run()