Load the listen address and port from settings, print connection guidance and explain startup failures.
117 lines
3.9 KiB
Python
117 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import socket
|
|
|
|
from server_config import load_config
|
|
|
|
DOCS_ARGUMENT = "docs:enabled"
|
|
|
|
|
|
def docs_enabled_from_args():
|
|
return DOCS_ARGUMENT in sys.argv[1:]
|
|
|
|
|
|
def _force_project_working_directory():
|
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
|
def _import_twisted():
|
|
try:
|
|
from twisted.internet import reactor
|
|
from twisted.web import server
|
|
except ImportError as exc:
|
|
raise SystemExit("Twisted is required. Install dependencies with: python3 -m pip install -r requirements.txt") from exc
|
|
return reactor, server
|
|
|
|
|
|
class Log:
|
|
def __init__(self):
|
|
os.makedirs("logs", exist_ok=True)
|
|
self.handle = open("logs/reximemo.log", "a", encoding="utf-8")
|
|
|
|
def write(self, text, silent=False):
|
|
line = "[%s] %s" % (time.strftime("%H:%M:%S"), str(text).rstrip())
|
|
if not silent:
|
|
print(line)
|
|
self.handle.write(line + "\n")
|
|
self.handle.flush()
|
|
|
|
def close(self):
|
|
self.handle.close()
|
|
|
|
|
|
def create_site(*, docs_enabled=False):
|
|
_force_project_working_directory()
|
|
reactor, twisted_server = _import_twisted()
|
|
import DB # initializes SQLite
|
|
import hatena
|
|
log = Log()
|
|
hatena.ServerLog = log
|
|
|
|
class ProxyCompatibleSite(twisted_server.Site):
|
|
def buildProtocol(self, addr):
|
|
protocol = super().buildProtocol(addr)
|
|
original = protocol.dataReceived
|
|
|
|
def data_received(data):
|
|
for host in (b"flipnote.hatena.com", b"ugomemo.hatena.ne.jp"):
|
|
data = data.replace(b"GET http://" + host, b"GET ")
|
|
data = data.replace(b"POST http://" + host, b"POST ")
|
|
return original(data)
|
|
|
|
protocol.dataReceived = data_received
|
|
return protocol
|
|
|
|
return reactor, ProxyCompatibleSite(hatena.Setup(docs_enabled=docs_enabled)), log
|
|
|
|
|
|
def main():
|
|
settings = load_config()
|
|
try:
|
|
port = int(os.environ.get("REXIMEMO_PORT", settings["port"]))
|
|
if not 1024 <= port <= 65535:
|
|
raise ValueError
|
|
except ValueError as exc:
|
|
raise SystemExit("Choose a port between 1024 and 65535 in server-settings.json or REXIMEMO_PORT.") from exc
|
|
host = os.environ.get("REXIMEMO_HOST", settings["host"])
|
|
docs_enabled = docs_enabled_from_args()
|
|
reactor, site, log = create_site(docs_enabled=docs_enabled)
|
|
log.write("%s starting on %s:%d" % (settings["site_name"], host, port))
|
|
log.write("Web client: http://127.0.0.1:%d/" % port)
|
|
try:
|
|
addresses = sorted({address[4][0] for address in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET)
|
|
if not address[4][0].startswith("127.")})
|
|
except OSError:
|
|
addresses = []
|
|
if host in {"0.0.0.0", "::"}:
|
|
for address in addresses:
|
|
log.write("DSi HTTP proxy: %s, port %d" % (address, port))
|
|
elif host not in {"127.0.0.1", "::1", "localhost"}:
|
|
log.write("DSi HTTP proxy: %s, port %d" % (host, port))
|
|
else:
|
|
log.write("Listening on this computer only. For a DSi on your network, choose listen address 0.0.0.0 during setup.")
|
|
if not settings["setup_complete"]:
|
|
log.write("First-run setup is available: python manage.py setup")
|
|
log.write("DSi mode uses the historical HTTP proxy path; NAS/DNS/auth services are not included.")
|
|
if docs_enabled:
|
|
log.write("Built-in documentation enabled at /docs")
|
|
from twisted.internet.error import CannotListenError
|
|
try:
|
|
reactor.listenTCP(port, site, interface=host)
|
|
except CannotListenError as exc:
|
|
log.close()
|
|
raise SystemExit(f"Could not listen on {host}:{port}. Another server may already use that port. Stop it or choose another port with: python manage.py setup") from exc
|
|
try:
|
|
reactor.run()
|
|
finally:
|
|
log.write("Server shutdown", True)
|
|
log.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
