-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
193 lines (167 loc) · 6.26 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python3
import argparse
import os
import signal
import sys
import tempfile
import time
import shutil
import subprocess
import json
import http.server
import socketserver
import threading
from urllib import request
from pathlib import Path
def parse_arguments():
parser = argparse.ArgumentParser(description="Serve content through ngrok")
parser.add_argument("-p", "--port", type=int, default=8080, help="Port to use (default: 8080)")
parser.add_argument("--basic-auth", type=str, help="Basic auth in format 'admin:password'")
parser.add_argument("content", nargs='*', help="Folder path or text content to serve (optional)")
return parser.parse_args()
def setup_content(args):
"""Prepare the content to be served"""
# Create temporary directory
temp_dir = tempfile.mkdtemp()
print(f"Created temporary directory: {temp_dir}")
# Join all content arguments in case it's text with spaces
content = " ".join(args.content)
# no content, open default editor
if not content:
tmp_file = Path(temp_dir) / "share.txt"
# no content, open default editor and use file on save
editor = os.environ.get("EDITOR", "vi")
subprocess.run([editor, tmp_file.as_posix()])
with open(tmp_file) as shared_file:
content = shared_file.read()
# Check if it's a folder
if os.path.isdir(content):
print(f"Detected folder input: {content}")
# Copy all files from source directory to temporary directory
for item in os.listdir(content):
src_path = os.path.join(content, item)
dst_path = os.path.join(temp_dir, item)
if os.path.isdir(src_path):
shutil.copytree(src_path, dst_path)
else:
shutil.copy2(src_path, dst_path)
# check if it's file
elif os.path.isfile(content):
print(f"Detected file input: {content}")
# Copy the file to the temporary directory
shutil.copy2(content, temp_dir)
else:
# It's text content
print("Detected text input, creating index.html")
with (Path(__file__).parent / "index.html").open("r") as src_file:
sample_content = src_file.read()
with open(os.path.join(temp_dir, "index.html"), "w") as f:
shared_content = sample_content.replace("<% message %>", content)
f.write(shared_content)
return temp_dir
class HttpServer:
def __init__(self, directory, port):
self.directory = directory
self.port = port
self.httpd = None
self.thread = None
def start(self):
"""Start the HTTP server in a separate thread"""
handler = http.server.SimpleHTTPRequestHandler
os.chdir(self.directory)
class CustomTCPServer(socketserver.TCPServer):
allow_reuse_address = True
self.httpd = CustomTCPServer(("", self.port), handler)
print(f"Starting HTTP server on port {self.port}...")
self.thread = threading.Thread(target=self.httpd.serve_forever)
self.thread.daemon = True
self.thread.start()
time.sleep(1) # Give the server a moment to start
return True
def stop(self):
"""Stop the HTTP server"""
if self.httpd:
self.httpd.shutdown()
self.httpd.server_close()
print("HTTP server stopped")
class NgrokTunnel:
def __init__(self, port, basic_auth=None):
self.port = port
self.process = None
self.basic_auth = basic_auth
def start(self):
"""Start ngrok tunnel"""
try:
command = ["ngrok", "http", str(self.port)]
if self.basic_auth:
command += ["--basic-auth", self.basic_auth]
# Start ngrok as a subprocess
self.process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
time.sleep(2) # Wait for ngrok to initialize
# Get the public URL
try:
with request.urlopen("http://localhost:4040/api/tunnels") as response:
data = response.read().decode('utf-8')
tunnels = json.loads(data)["tunnels"]
if tunnels:
print("\nSend this url to access message:")
for tunnel in tunnels:
print(f" {tunnel['public_url']}")
return True
else:
return False
except Exception as e:
print(f"[error] Failed to get ngrok tunnel info exception: '{e}'")
for message in self.process.communicate():
print(f"[error] {message}")
return False
except FileNotFoundError:
print("Error: ngrok not found. Please install ngrok first.")
return False
def stop(self):
"""Stop the ngrok tunnel"""
if self.process:
self.process.terminate()
print("Ngrok tunnel closed")
def main():
args = parse_arguments()
temp_dir = setup_content(args)
# Set up cleanup handler
def cleanup_handler(signum, frame):
print("\nCleaning up...")
http_server.stop()
ngrok_tunnel.stop()
shutil.rmtree(temp_dir)
sys.exit(0)
# Register signal handlers
signal.signal(signal.SIGINT, cleanup_handler)
signal.signal(signal.SIGTERM, cleanup_handler)
# Start HTTP server
http_server = HttpServer(temp_dir, args.port)
if not http_server.start():
print("Failed to start HTTP server")
shutil.rmtree(temp_dir)
return
# Start ngrok
ngrok_tunnel = NgrokTunnel(
port=args.port,
basic_auth=args.basic_auth,
)
if not ngrok_tunnel.start():
print("Failed to start ngrok tunnel")
http_server.stop()
shutil.rmtree(temp_dir)
return
print("\nPress Ctrl+C to stop the server and exit")
try:
# Keep the main thread alive
while True:
time.sleep(1)
except KeyboardInterrupt:
cleanup_handler(None, None)
if __name__ == "__main__":
main()