Skip to content

Commit dbfc60f

Browse files
...
1 parent 97086cd commit dbfc60f

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

app_test.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from flask import Flask, render_template_string
2+
import requests
3+
import socket
4+
from zeroconf import ServiceInfo, Zeroconf, ServiceBrowser
5+
import threading
6+
7+
app = Flask(__name__)
8+
9+
# --- CONFIGURATION ---
10+
THIS_NAME = "KIDA00" # Change per app
11+
THIS_PORT = 5003 # Change per app
12+
TYPE = "_flask-link._tcp.local."
13+
14+
found_servers = {}
15+
16+
def get_ip():
17+
"""Helper to find the actual local network IP address"""
18+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
19+
try:
20+
# doesn't even have to be reachable
21+
s.connect(('10.255.255.255', 1))
22+
IP = s.getsockname()[0]
23+
except Exception:
24+
IP = '127.0.0.1'
25+
finally:
26+
s.close()
27+
return IP
28+
29+
class MyListener:
30+
def remove_service(self, zeroconf, type, name):
31+
short_name = name.split('.')[0]
32+
if short_name in found_servers:
33+
del found_servers[short_name]
34+
35+
def add_service(self, zeroconf, type, name):
36+
info = zeroconf.get_service_info(type, name)
37+
if info:
38+
# Convert binary IP to string
39+
addresses = [socket.inet_ntoa(addr) for addr in info.addresses]
40+
if addresses:
41+
short_name = name.split('.')[0]
42+
if short_name != THIS_NAME:
43+
found_servers[short_name] = f"http://{addresses[0]}:{info.port}"
44+
45+
# --- ZEROCONF SETUP ---
46+
my_ip = get_ip()
47+
print(f"Starting {THIS_NAME} on {my_ip}:{THIS_PORT}")
48+
49+
zeroconf = Zeroconf()
50+
info = ServiceInfo(
51+
TYPE,
52+
f"{THIS_NAME}.{TYPE}",
53+
addresses=[socket.inet_aton(my_ip)],
54+
port=THIS_PORT,
55+
properties={'version': '1.0'}
56+
)
57+
zeroconf.register_service(info)
58+
browser = ServiceBrowser(zeroconf, TYPE, MyListener())
59+
60+
@app.route("/")
61+
def dashboard():
62+
status_html = f'<div style="margin:10px;"><span style="color:green;">●</span> <b>{THIS_NAME}</b> (Self)</div>'
63+
64+
# Copy dict keys to avoid "dictionary changed size during iteration" errors
65+
for name in list(found_servers.keys()):
66+
url = found_servers[name]
67+
try:
68+
r = requests.get(f"{url}/ping", timeout=0.5)
69+
if r.status_code == 200:
70+
status_html += f'<div style="margin:10px;"><span style="color:green;">●</span> <b>{name}</b> Online</div>'
71+
except:
72+
continue
73+
74+
return render_template_string(f"""
75+
<html>
76+
<head>
77+
<script>
78+
setTimeout(function(){{ window.location.reload(1); }}, 3000);
79+
</script>
80+
</head>
81+
<body style="text-align:center; font-family:sans-serif; padding-top:50px; background-color:#f4f4f9;">
82+
<div style="display:inline-block; padding:20px; border-radius:15px; background:white; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
83+
<h1>Active Network</h1>
84+
<p style="color:gray; font-size:0.8em;">My IP: {my_ip}</p>
85+
<hr>
86+
{status_html}
87+
</div>
88+
</body>
89+
</html>
90+
""")
91+
92+
@app.route("/ping")
93+
def ping():
94+
return f"{THIS_NAME} alive"
95+
96+
if __name__ == "__main__":
97+
try:
98+
# Use threaded=True so the ping requests don't block the UI
99+
app.run(host="0.0.0.0", port=THIS_PORT, debug=False, threaded=True)
100+
finally:
101+
print("Shutting down...")
102+
zeroconf.unregister_service(info)
103+
zeroconf.close()

0 commit comments

Comments
 (0)