-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathhomedock.py
More file actions
264 lines (207 loc) · 10.7 KB
/
homedock.py
File metadata and controls
264 lines (207 loc) · 10.7 KB
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
"""
homedock.py
Copyright © 2023-2026 Banshee, All Rights Reserved
See LICENSE.md or https://polyformproject.org/licenses/strict/1.0.0/
https://www.banshee.pro
"""
import os
import signal
import logging
import asyncio
import threading
from datetime import timedelta
from flask import g
from flask_compress import Compress
from hypercorn.asyncio import serve
from hypercorn.config import Config
from hypercorn.middleware import AsyncioWSGIMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
from vite_fusion import register_vite_assets
from pymodules.hd_HDOSWebServerInit import homedock_www
from pymodules.hd_AppFilters import b64encode_filter
from pymodules.hd_FunctionsGlobals import current_directory, version, version_hash, running_OS, running_ARCH, logs_folder
from pymodules.hd_FunctionsNetwork import local_ip, internet_ip
from pymodules.hd_PublicKeySender import send_public_key
from pymodules.hd_FunctionsConfig import check_and_generate_config, read_config
from pymodules.hd_FunctionsActiveInstance import active_instance
from pymodules.hd_FunctionsMain import validate_docker_installation, validate_docker_compose_installation, init_color_if_windows
from pymodules.hd_FunctionsInitUserFolders import init_all_directories
from pymodules.hd_ThreadContainerResourceUsage import start_resource_usage_thread
from pymodules.hd_ThreadAutoPortRouting import start_auto_port_routing_thread
from pymodules.hd_ThreadAppUpdatesChecker import start_app_updates_checker_thread
from pymodules.hd_ThreadNotificationsFetcher import start_notifications_fetcher_thread
from pymodules.hd_RouteModules import RouteAllModules
from pymodules.hd_EnterpriseLoader import load_enterprise, print_enterprise_banner
from pymodules.hd_UpdateDeps import check_and_update_dependencies
from pymodules.hd_FunctionsNativeSSL import ssl_enabled, get_ssl_cert_info, get_ssl_cert_directory
from pymodules.hd_ThreadZeroConf import announce_homedock_service, format_url
from pymodules.hd_HMRUpdate import set_updating_state
from pymodules.hd_NonceGenerator import setup_nonce
from pymodules.hd_CSPMaxed import setup_security_headers
from pymodules.hd_HTMLErrorCodeHandler import setup_error_handlers
from pymodules.hd_ApplyUploadLimits import ContentSizeLimitMiddleware, FlaskDevUploadLimitMiddleware
from pymodules.hd_FunctionsHostSelector import is_docker
os.chdir(current_directory)
set_updating_state(False)
check_and_generate_config()
globalConfig = read_config()
init_all_directories()
logging.basicConfig(filename=os.path.join(logs_folder, "error.log"), level=logging.ERROR)
homedock_www = homedock_www
homedock_www.add_template_filter(b64encode_filter, name="b64encode")
Compress(homedock_www)
init_color_if_windows()
validate_docker_installation()
validate_docker_compose_installation()
setup_nonce(homedock_www)
setup_security_headers(homedock_www, globalConfig)
setup_error_handlers(homedock_www, read_config, version_hash)
active_instance()
register_vite_assets(homedock_www, dev_mode=globalConfig["run_on_development"], dev_server_url="http://localhost:5173", dist_path="/homedock-ui/vue3/dist", manifest_path="homedock-ui/vue3/dist/.vite/manifest.json", nonce_provider=lambda: g.get("nonce"), logger=None)
if __name__ == "__main__":
check_and_update_dependencies()
RouteAllModules(homedock_www, send_public_key)
enterprise_cogs = load_enterprise(homedock_www)
start_auto_port_routing_thread()
start_resource_usage_thread()
start_app_updates_checker_thread()
start_notifications_fetcher_thread()
user_name = globalConfig["user_name"]
run_port = globalConfig["run_port"]
local_dns = globalConfig["local_dns"]
dynamic_dns = globalConfig["dynamic_dns"]
run_on_development = globalConfig["run_on_development"]
reverse_proxy_enabled = globalConfig.get("reverse_proxy", False)
ssl_enabled_var = ssl_enabled()
if ssl_enabled_var and run_port == 80:
print()
print(" ! SSL enabled on port 80, hard switching to 443")
run_port = 443
protocol = "https" if ssl_enabled_var else "http"
print()
print(" @@@@@@@@@@@@@@@@@@@@@@@@ ")
print(" @@@@@@@@@@@@@@@@@@@@@@@@@ ")
print(" @@@@ ")
print(" @@@@ @@@@@@@@@@@@@@@@@@@@ ")
print(" @@@@ @@@ ")
print(" @@@ @@@ @@@@@@@@@@@@@ ")
print(" @@@ @@@* @@@@ @@@* @ ")
print(" @@@ @@@@ @@@@ @@@@ @@ ")
print(" @@@* @@@@ (@@@ @@@@@@@@@ ")
print(" @@@@ @@@@ @@@ ////////// ")
print(" @@@@ @@@@ @@@ ")
print(" @@@@ #@@@ @@@ ")
print(" @@@@ @@@ @@@ ")
print()
print(" Copyright © 2023-2026 Banshee, All Rights Reserved ")
print()
print(" ▸ Repo: https://github.com/BansheeTech/HomeDockOS")
print(" ▸ Web: https://www.homedock.cloud")
print(" ▸ Docs: https://docs.homedock.cloud")
print(" ▸ Discord: https://discord.gg/Zj3JCYsRWw")
print(" ▸ Support: support@homedock.cloud")
print()
print(" ⌂ \033[1;32;40mHomeDock OS Version\033[0m:", version)
print(" ~ \033[1;30mVersion Hash: " + version_hash + "\033[0m")
print_enterprise_banner(enterprise_cogs)
print()
print(" * Run from:", current_directory)
print(" * Run on port:", run_port)
print(" * Run on local IP:", local_ip)
print(" * Run on public IP:", internet_ip)
print(" * Run on Native SSL:", ssl_enabled_var)
print(" * Run on development mode:", run_on_development)
if reverse_proxy_enabled:
print(" * Reverse Proxy support:", reverse_proxy_enabled)
print()
print(" * CPU Type:", running_ARCH)
print(" * Underlying OS:", running_OS)
print()
print(" * User Login:", user_name)
print(" * Default Password:", "passwd")
print()
if ssl_enabled_var:
cert_path = os.path.join(get_ssl_cert_directory(), "fullchain.pem")
cert_info = get_ssl_cert_info(cert_path)
print(" » SSL Certificate Information:")
if "error" in cert_info:
print(f' └─ \x1b[4mError: {cert_info["error"]}\x1b[0m')
else:
print(f' ├─ \x1b[4mValid Until: {cert_info["notAfter"]}\x1b[0m')
print(f' └─ \x1b[4mIssuer: {cert_info["issuerO"]} V{cert_info["version"]} ({cert_info["issuerCN"]})\x1b[0m')
print()
print(f" + Log in at: \x1b[4m{format_url(protocol, local_ip, run_port)}\x1b[0m")
print(f" ├─ \x1b[4m{format_url(protocol, internet_ip, run_port)}\x1b[0m")
print(f" └─ \x1b[4m{format_url(protocol, dynamic_dns, run_port)}\x1b[0m")
if local_dns:
thread_result = {"success": False}
def run_service():
thread_result["success"] = announce_homedock_service()
thread = threading.Thread(target=run_service, daemon=True)
thread.start()
thread.join()
if thread_result["success"]:
print(f" > \x1b[4m{format_url(protocol, 'homedock.local', run_port)}\x1b[0m")
else:
print(" ! homedock.local unavailable")
if is_docker:
print()
print(" \033[1;33;40m» Running in Docker-in-Docker mode\033[0m")
print(" HomeDock OS is intended to run directly on your machine.")
print(" If you like it, install it natively for the best experience!")
print(" Available for Windows, macOS and Linux at:")
print(" \x1b[4mhttps://www.homedock.cloud/install\x1b[0m")
print()
homedock_www.config["PERMANENT_SESSION_LIFETIME"] = timedelta(hours=24)
homedock_www.config["SECRET_KEY"] = os.urandom(32)
homedock_www.config["SESSION_REFRESH_EACH_REQUEST"] = False
homedock_www.config["SESSION_COOKIE_HTTPONLY"] = True
homedock_www.config["SESSION_COOKIE_SAMESITE"] = "Strict"
homedock_www.config["SESSION_COOKIE_NAME"] = "homedock_session"
homedock_www.config["SERVER_NAME"] = None
homedock_www.config["SESSION_TYPE"] = "filesystem"
if ssl_enabled() or reverse_proxy_enabled:
homedock_www.config["SESSION_COOKIE_SECURE"] = True # Secure Flag for HTTPS or reverse proxy TLS termination
try:
if run_on_development:
FlaskDevUploadLimitMiddleware(homedock_www)
homedock_www.run(host="0.0.0.0", port=run_port, debug=True, use_reloader=False)
else:
hypercorn_config = Config()
hypercorn_config.loglevel = "DEBUG"
hypercorn_config.include_server_header = False
hypercorn_config.bind = [f"0.0.0.0:{run_port}"]
redirect_app = redirect_config = None
if ssl_enabled_var:
ssl_cert_dir = get_ssl_cert_directory()
hypercorn_config.certfile = os.path.join(ssl_cert_dir, "fullchain.pem")
hypercorn_config.keyfile = os.path.join(ssl_cert_dir, "privkey.pem")
hypercorn_config.ca_certs = os.path.join(ssl_cert_dir, "chain.pem")
if run_port == 443:
from pymodules.hd_HTTPRedirector import start_http_redirect_server
redirect_app, redirect_config = start_http_redirect_server()
async def homedock_www_asgi(scope, receive, send):
wsgi_app = homedock_www
if reverse_proxy_enabled:
wsgi_app = ProxyFix(homedock_www, x_for=1, x_proto=1, x_host=1, x_port=0, x_prefix=0)
app = AsyncioWSGIMiddleware(wsgi_app, max_body_size=1 * 1024 * 1024 * 1024)
await ContentSizeLimitMiddleware(app)(scope, receive, send)
async def run_all_servers():
from concurrent.futures import ThreadPoolExecutor
asyncio.get_running_loop().set_default_executor(ThreadPoolExecutor(max_workers=50))
stop_event = asyncio.Event()
loop = asyncio.get_running_loop()
for sig in (signal.SIGINT, signal.SIGTERM):
try:
loop.add_signal_handler(sig, stop_event.set)
except NotImplementedError:
pass # HDOS00001
await asyncio.gather(*(serve(app, cfg, shutdown_trigger=stop_event.wait) for app, cfg in [(redirect_app, redirect_config), (homedock_www_asgi, hypercorn_config)] if app and cfg))
print(" ✓ Servers shut down SIGTERM received")
asyncio.run(run_all_servers())
except OSError as e:
if e.errno == 98:
print("Error: Selected port >", run_port, "< is already in use by another service/application!")
print("Please select any other port by modifying homedock_server.conf!")
else:
print("Unexpected error occurred: ", e)