globalhost 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
globalhost/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .core import GlobalHostApp
2
+
3
+ __version__ = "0.1.0"
4
+ __all__ = ["GlobalHostApp"]
globalhost/core.py ADDED
@@ -0,0 +1,73 @@
1
+ import sys
2
+ import os
3
+ import subprocess
4
+ import logging
5
+ from .tunnel import TunnelManager
6
+
7
+ # Root logger configuration for the globalhost package
8
+ logger = logging.getLogger("globalhost")
9
+
10
+ class GlobalHostApp:
11
+ def __init__(self):
12
+ self.tunnel = TunnelManager()
13
+ self.url = None
14
+
15
+ def launch(self, framework: str, app: str, port: int = 8000):
16
+ framework = framework.lower()
17
+
18
+ try:
19
+ # 1. Start tunnel and grab URL
20
+ self.url = self.tunnel.start(port=port)
21
+
22
+ logger.info(f"\nšŸš€ GLOBALHOST IS LIVE!\nšŸ‘‰ YOUR PUBLIC URL: {self.url} šŸ‘ˆ\n")
23
+
24
+ # 2. Spin up framework
25
+ if framework == "fastapi":
26
+ logger.info(f"Launching FastAPI backend via Uvicorn on port {port}...")
27
+
28
+ # Get the directory of the currently running script
29
+ script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
30
+
31
+ subprocess.run(
32
+ [sys.executable, "-m", "uvicorn", app, "--host", "127.0.0.1", "--port", str(port)],
33
+ cwd=script_dir # šŸ‘ˆ Run Uvicorn from the script's directory
34
+ )
35
+ elif framework == "django":
36
+ logger.info(f"Launching Django server on port {port}...")
37
+
38
+ if os.path.exists("manage.py"):
39
+ subprocess.run([sys.executable, "manage.py", "runserver", f"127.0.0.1:{port}"])
40
+ else:
41
+ logger.info("No manage.py detected. Booting Django in standalone mode...")
42
+
43
+ # Clean up file paths or standard dot notation signatures
44
+ clean_app = app.replace("\\", "/").rstrip(".py")
45
+
46
+ if "/" in clean_app:
47
+ module_dir = os.path.dirname(os.path.abspath(clean_app))
48
+ module_name = os.path.basename(clean_app)
49
+ else:
50
+ module_dir = os.getcwd()
51
+ module_name = clean_app
52
+
53
+ # Inject the target example directory directly into the python process environment map
54
+ env = os.environ.copy()
55
+ env["PYTHONPATH"] = module_dir + os.pathsep + env.get("PYTHONPATH", "")
56
+
57
+ subprocess.run([
58
+ sys.executable, "-m", "django",
59
+ "runserver", f"127.0.0.1:{port}",
60
+ f"--settings={module_name}"
61
+ ], env=env)
62
+ else:
63
+ logger.error(f"Unknown framework '{framework}'")
64
+ self.tunnel.stop()
65
+ sys.exit(1)
66
+
67
+ except KeyboardInterrupt:
68
+ self.tunnel.stop()
69
+ logger.info("GlobalHost safely shut down.")
70
+ except Exception as e:
71
+ self.tunnel.stop()
72
+ logger.error(f"GlobalHost unexpected crash: {e}")
73
+ sys.exit(1)
globalhost/tunnel.py ADDED
@@ -0,0 +1,101 @@
1
+ import os
2
+ import platform
3
+ import subprocess
4
+ import time
5
+ import sys
6
+ import urllib.request
7
+ import logging
8
+
9
+ # Set up a named logger for this module
10
+ logger = logging.getLogger("globalhost.tunnel")
11
+
12
+ class TunnelManager:
13
+ def __init__(self):
14
+ self.system = platform.system().lower()
15
+ self.machine = platform.machine().lower()
16
+ self.process = None
17
+ self.public_url = None
18
+
19
+ def _get_binary_config(self):
20
+ base_url = "https://github.com/cloudflare/cloudflared/releases/latest/download/"
21
+ if "windows" in self.system:
22
+ filename = "cloudflared-windows-amd64.exe"
23
+ return filename, base_url + filename
24
+ elif "linux" in self.system:
25
+ filename = "cloudflared-linux-arm64" if "arm" in self.machine or "aarch64" in self.machine else "cloudflared-linux-amd64"
26
+ return filename, base_url + filename
27
+ elif "darwin" in self.system:
28
+ filename = "cloudflared-darwin-arm64.tgz" if "arm" in self.machine else "cloudflared-darwin-amd64.tgz"
29
+ return filename, base_url + filename
30
+ else:
31
+ raise RuntimeError(f"GlobalHost doesn't support your OS yet: {self.system}")
32
+
33
+ def _ensure_binary_exists(self) -> str:
34
+ base_dir = os.path.dirname(__file__)
35
+ bin_dir = os.path.join(base_dir, "bin")
36
+ os.makedirs(bin_dir, exist_ok=True)
37
+
38
+ filename, download_url = self._get_binary_config()
39
+ binary_path = os.path.join(bin_dir, filename)
40
+
41
+ if filename.endswith(".tgz"):
42
+ binary_path = os.path.join(bin_dir, "cloudflared")
43
+
44
+ if not os.path.exists(binary_path):
45
+ logger.info("Core networking module missing. Downloading from Cloudflare network...")
46
+ logger.debug(f"Downloading from URL: {download_url}")
47
+ try:
48
+ temp_download = os.path.join(bin_dir, filename)
49
+ urllib.request.urlretrieve(download_url, temp_download)
50
+ if filename.endswith(".tgz"):
51
+ import tarfile
52
+ with tarfile.open(temp_download, "r:gz") as tar:
53
+ tar.extractall(path=bin_dir)
54
+ os.remove(temp_download)
55
+ logger.info("Download complete.")
56
+ except Exception as e:
57
+ logger.error(f"Failed to download network binary: {e}")
58
+ sys.exit(1)
59
+
60
+ if "windows" not in self.system:
61
+ os.chmod(binary_path, 0o755)
62
+
63
+ return binary_path
64
+
65
+ def start(self, port: int) -> str:
66
+ binary = self._ensure_binary_exists()
67
+
68
+ logger.info(f"Establishing secure edge network tunnel to port {port}...")
69
+
70
+ self.process = subprocess.Popen(
71
+ [binary, "tunnel", "--url", f"http://localhost:{port}"],
72
+ stdout=subprocess.PIPE,
73
+ stderr=subprocess.STDOUT,
74
+ text=True,
75
+ bufsize=1
76
+ )
77
+
78
+ start_time = time.time()
79
+ while time.time() - start_time < 10:
80
+ line = self.process.stdout.readline()
81
+ if not line:
82
+ break
83
+
84
+ if ".trycloudflare.com" in line:
85
+ for word in line.split():
86
+ if "trycloudflare.com" in word:
87
+ url = word.strip()
88
+ if url.startswith("https://"):
89
+ self.public_url = url
90
+ else:
91
+ self.public_url = f"https://{url}"
92
+ break
93
+ break
94
+
95
+ return self.public_url
96
+
97
+ def stop(self):
98
+ if self.process:
99
+ logger.info("Collapsing tunnel, removing machine from public routing...")
100
+ self.process.terminate()
101
+ self.process.wait()
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: globalhost
3
+ Version: 0.1.0
4
+ Summary: Turn any machine into a public server instantly with automated zero-config tunnels.
5
+ Author-email: Joel Opoku <joelclouds@gmail.com>
6
+ Project-URL: Homepage, https://github.com/joelclouds/globalhost
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: uvicorn>=0.22.0
13
+
14
+ # GlobalHost šŸ›°ļø
15
+
16
+ Turn any machine running your code into a publicly accessible, secure SaaS backend instantly. No `localhost`, no router configuration, and zero cloud deployment setup needed during development.
17
+
18
+ ## Features
19
+ * **Zero-Config Public Ingress:** Automatically spawns a secure, temporary public HTTPS tunnel (`.trycloudflare.com`) on startup.
20
+ * **Framework Agnostic:** Works flawlessly out-of-the-box with **FastAPI**, **Django**, Flask, or raw Python scripts.
21
+ * **Self-Contained & Lightweight:** Auto-provisions the exact native binary for your OS (Mac/Win/Lin) on the fly. No `brew`, `apt`, or manual downloads required, and keeps the pip package tiny.
22
+
23
+ ---
24
+
25
+ ## Architecture
26
+
27
+ ```text
28
+ [ Any PC Running Your Code ] ──(Outbound Tunnel)──> [ Global Edge Network ] ──> [ Public Internet URL ]
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Quick Start
34
+
35
+ ### 1. Installation
36
+
37
+ ```bash
38
+ pip install globalhost
39
+ ```
40
+
41
+ ### 2. Basic Usage (Zero-Config)
42
+
43
+ Drop this into your project entrypoint. This automatically boots the tunnel and wraps your web framework.
44
+
45
+ ```python
46
+ from globalhost import GlobalHostApp
47
+ from fastapi import FastAPI
48
+
49
+ app = FastAPI()
50
+
51
+ @app.get("/my-algo")
52
+ def run_algo():
53
+ return {"status": "Your golden algo is live globally!"}
54
+
55
+ if __name__ == "__main__":
56
+ # Boots tunnel and starts the server
57
+ GlobalHostApp.launch(framework="fastapi", app="main:app", port=8000)
58
+ ```
59
+
60
+ ### 3. Programmatic Usage (Advanced)
61
+
62
+ Need to inject the live public URL into a database, webhook, or frontend config at runtime? Use the programmatic API:
63
+
64
+ ```python
65
+ from globalhost import GlobalHostApp
66
+
67
+ gh = GlobalHostApp()
68
+
69
+ # Start the tunnel and grab the endpoint string
70
+ public_endpoint = gh.tunnel.start(port=9000)
71
+
72
+ print(f"šŸ”— Live Hyperlink: {public_endpoint}")
73
+ print(f"šŸ“¦ Stored Attribute: {gh.tunnel.public_url}")
74
+
75
+ # Keep tunnel alive while your app runs...
76
+ ```
77
+
78
+ ---
79
+
80
+ ## How It Works Under the Hood
81
+
82
+ 1. **OS Detection:** On execution, the module identifies the hosting machine's operating system and architecture.
83
+ 2. **Auto-Provisioning:** It silently downloads and caches the exact lightweight network binary required for that specific system.
84
+ 3. **Reverse Tunnel Routing:** It executes the binary to establish a secure outbound connection to a global edge router, bypassing local firewalls and printing your public production-ready endpoint straight to the console.
85
+
86
+ ---
87
+
88
+ ## Examples
89
+
90
+ Ready-to-run implementations are available in the `examples/` directory:
91
+ * **FastAPI:** `examples/fastapi/01_json_api.py` & `02_webpage.py`
92
+ * **Django:** `examples/django/01_json_api.py` & `02_webpage.py`
93
+ * **Runtime URL Extraction:** `examples/runtime_url.py`
94
+
95
+ ---
96
+
97
+ ## Author
98
+
99
+ **Joel Opoku**
100
+ šŸ“§ [joelclouds@gmail.com](mailto:joelclouds@gmail.com)
101
+ 🌐 [joelclouds.pythonanywhere.com](https://joelclouds.pythonanywhere.com)
102
+ šŸ™ [github.com/joelclouds/globalhost](https://github.com/joelclouds/globalhost)
103
+
104
+ ## License
105
+
106
+ Distributed under the MIT License. See `LICENSE` for more information.
@@ -0,0 +1,7 @@
1
+ globalhost/__init__.py,sha256=oTz_h5NEmfEtHDY66QWrSD7P71EMmMPf7LxiR1DLaWA,83
2
+ globalhost/core.py,sha256=A2mX7_5Vn-LxeyavJTRFkub8OL-Us4ssLrY11-E9pq4,2885
3
+ globalhost/tunnel.py,sha256=LZK_CpxDxMzp8UJKKj2UyNoSDV4lZGLoIaM6TZN0Nvk,3741
4
+ globalhost-0.1.0.dist-info/METADATA,sha256=A-Hr_JV_W5wDNXMO59T1BX6JCvHb739yPidwP1D9fUE,3470
5
+ globalhost-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ globalhost-0.1.0.dist-info/top_level.txt,sha256=c4InFFLegEgznGRj3zG1ksGe_1lYpWJb25ZsX_W4jOA,11
7
+ globalhost-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ globalhost