diff --git a/aprsd/client.py b/aprsd/client.py index 9c9522d..08df034 100644 --- a/aprsd/client.py +++ b/aprsd/client.py @@ -152,9 +152,10 @@ class APRSISClient(Client): except LoginError as e: LOG.error(f"Failed to login to APRS-IS Server '{e}'") connected = False - raise e + time.sleep(backoff) except Exception as e: LOG.error(f"Unable to connect to APRS-IS server. '{e}' ") + connected = False time.sleep(backoff) backoff = backoff * 2 continue diff --git a/aprsd/clients/aprsis.py b/aprsd/clients/aprsis.py index 5ba53b1..aa1a9ba 100644 --- a/aprsd/clients/aprsis.py +++ b/aprsd/clients/aprsis.py @@ -112,23 +112,23 @@ class Aprsdis(aprslib.IS): self._sendall(login_str) self.sock.settimeout(5) test = self.sock.recv(len(login_str) + 100) + self.logger.debug("Server: '%s'", test) if is_py3: test = test.decode("latin-1") test = test.rstrip() - self.logger.debug("Server: %s", test) + self.logger.debug("Server: '%s'", test) - a, b, callsign, status, e = test.split(" ", 4) + if not test: + raise LoginError(f"Server Response Empty: '{test}'") + + _, _, callsign, status, e = test.split(" ", 4) s = e.split(",") if len(s): server_string = s[0].replace("server ", "") else: server_string = e.replace("server ", "") - self.logger.info(f"Connected to {server_string}") - self.server_string = server_string - stats.APRSDStats().set_aprsis_server(server_string) - if callsign == "": raise LoginError("Server responded with empty callsign???") if callsign != self.callsign: @@ -141,6 +141,10 @@ class Aprsdis(aprslib.IS): else: self.logger.info("Login successful") + self.logger.info(f"Connected to {server_string}") + self.server_string = server_string + stats.APRSDStats().set_aprsis_server(server_string) + except LoginError as e: self.logger.error(str(e)) self.close() @@ -148,6 +152,7 @@ class Aprsdis(aprslib.IS): except Exception as e: self.close() self.logger.error(f"Failed to login '{e}'") + self.logger.exception(e) raise LoginError("Failed to login") def consumer(self, callback, blocking=True, immortal=False, raw=False): diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index c0e719d..fbdc87d 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -12,7 +12,6 @@ import click import flask from flask import request from flask.logging import default_handler -import flask_classful from flask_httpauth import HTTPBasicAuth from flask_socketio import Namespace, SocketIO from oslo_config import cfg @@ -31,9 +30,16 @@ from aprsd.utils import objectstore, trace CONF = cfg.CONF LOG = logging.getLogger("APRSD") auth = HTTPBasicAuth() -users = None +users = {} socketio = None +flask_app = flask.Flask( + "aprsd", + static_url_path="/static", + static_folder="web/chat/static", + template_folder="web/chat/templates", +) + def signal_handler(sig, frame): @@ -174,106 +180,108 @@ class WebChatProcessPacketThread(rx.APRSDProcessPacketThread): ) -class WebChatFlask(flask_classful.FlaskView): +def set_config(): + global users - def set_config(self): - global users - self.users = {} - user = CONF.admin.user - self.users[user] = generate_password_hash(CONF.admin.password) - users = self.users - def _get_transport(self, stats): - if CONF.aprs_network.enabled: - transport = "aprs-is" - aprs_connection = ( - "APRS-IS Server: " - "{}".format(stats["stats"]["aprs-is"]["server"]) - ) - else: - # We might be connected to a KISS socket? - if client.KISSClient.is_enabled(): - transport = client.KISSClient.transport() - if transport == client.TRANSPORT_TCPKISS: - aprs_connection = ( - "TCPKISS://{}:{}".format( - CONF.kiss_tcp.host, - CONF.kiss_tcp.port, - ) - ) - elif transport == client.TRANSPORT_SERIALKISS: - # for pep8 violation - aprs_connection = ( - "SerialKISS://{}@{} baud".format( - CONF.kiss_serial.device, - CONF.kiss_serial.baudrate, - ), - ) - - return transport, aprs_connection - - @auth.login_required - def index(self): - ua_str = request.headers.get("User-Agent") - # this takes about 2 seconds :( - user_agent = ua_parse(ua_str) - LOG.debug(f"Is mobile? {user_agent.is_mobile}") - stats = self._stats() - - if user_agent.is_mobile: - html_template = "mobile.html" - else: - html_template = "index.html" - - # For development - # html_template = "mobile.html" - - LOG.debug(f"Template {html_template}") - - transport, aprs_connection = self._get_transport(stats) - LOG.debug(f"transport {transport} aprs_connection {aprs_connection}") - - stats["transport"] = transport - stats["aprs_connection"] = aprs_connection - LOG.debug(f"initial stats = {stats}") - - return flask.render_template( - html_template, - initial_stats=stats, - aprs_connection=aprs_connection, - callsign=CONF.callsign, - version=aprsd.__version__, +def _get_transport(stats): + if CONF.aprs_network.enabled: + transport = "aprs-is" + aprs_connection = ( + "APRS-IS Server: " + "{}".format(stats["stats"]["aprs-is"]["server"]) ) + else: + # We might be connected to a KISS socket? + if client.KISSClient.is_enabled(): + transport = client.KISSClient.transport() + if transport == client.TRANSPORT_TCPKISS: + aprs_connection = ( + "TCPKISS://{}:{}".format( + CONF.kiss_tcp.host, + CONF.kiss_tcp.port, + ) + ) + elif transport == client.TRANSPORT_SERIALKISS: + # for pep8 violation + aprs_connection = ( + "SerialKISS://{}@{} baud".format( + CONF.kiss_serial.device, + CONF.kiss_serial.baudrate, + ), + ) - @auth.login_required - def send_message_status(self): - LOG.debug(request) - msgs = SentMessages() - info = msgs.get_all() - return json.dumps(info) + return transport, aprs_connection - def _stats(self): - stats_obj = stats.APRSDStats() - now = datetime.datetime.now() - time_format = "%m-%d-%Y %H:%M:%S" - stats_dict = stats_obj.stats() - # Webchat doesnt need these - del stats_dict["aprsd"]["watch_list"] - del stats_dict["aprsd"]["seen_list"] - # del stats_dict["email"] - # del stats_dict["plugins"] - # del stats_dict["messages"] +@auth.login_required +@flask_app.route("/") +def index(): + ua_str = request.headers.get("User-Agent") + # this takes about 2 seconds :( + user_agent = ua_parse(ua_str) + LOG.debug(f"Is mobile? {user_agent.is_mobile}") + stats = _stats() - result = { - "time": now.strftime(time_format), - "stats": stats_dict, - } + if user_agent.is_mobile: + html_template = "mobile.html" + else: + html_template = "index.html" - return result + # For development + # html_template = "mobile.html" - def stats(self): - return json.dumps(self._stats()) + LOG.debug(f"Template {html_template}") + + transport, aprs_connection = _get_transport(stats) + LOG.debug(f"transport {transport} aprs_connection {aprs_connection}") + + stats["transport"] = transport + stats["aprs_connection"] = aprs_connection + LOG.debug(f"initial stats = {stats}") + + return flask.render_template( + html_template, + initial_stats=stats, + aprs_connection=aprs_connection, + callsign=CONF.callsign, + version=aprsd.__version__, + ) + + +@auth.login_required +@flask_app.route("//send-message-status") +def send_message_status(): + LOG.debug(request) + msgs = SentMessages() + info = msgs.get_all() + return json.dumps(info) + + +def _stats(): + stats_obj = stats.APRSDStats() + now = datetime.datetime.now() + + time_format = "%m-%d-%Y %H:%M:%S" + stats_dict = stats_obj.stats() + # Webchat doesnt need these + del stats_dict["aprsd"]["watch_list"] + del stats_dict["aprsd"]["seen_list"] + # del stats_dict["email"] + # del stats_dict["plugins"] + # del stats_dict["messages"] + + result = { + "time": now.strftime(time_format), + "stats": stats_dict, + } + + return result + + +@flask_app.route("/stats") +def get_stats(): + return json.dumps(_stats()) class SendMessageNamespace(Namespace): @@ -377,21 +385,9 @@ def setup_logging(flask_app, loglevel, quiet): @trace.trace def init_flask(loglevel, quiet): - global socketio + global socketio, flask_app - flask_app = flask.Flask( - "aprsd", - static_url_path="/static", - static_folder="web/chat/static", - template_folder="web/chat/templates", - ) setup_logging(flask_app, loglevel, quiet) - server = WebChatFlask() - server.set_config() - flask_app.route("/", methods=["GET"])(server.index) - flask_app.route("/stats", methods=["GET"])(server.stats) - # flask_app.route("/send-message", methods=["GET"])(server.send_message) - flask_app.route("/send-message-status", methods=["GET"])(server.send_message_status) socketio = SocketIO( flask_app, logger=False, engineio_logger=False, @@ -407,7 +403,7 @@ def init_flask(loglevel, quiet): "/sendmsg", ), ) - return socketio, flask_app + return socketio # main() ### @@ -448,6 +444,8 @@ def webchat(ctx, flush, port): LOG.info(f"APRSD Started version: {aprsd.__version__}") CONF.log_opt_values(LOG, logging.DEBUG) + user = CONF.admin.user + users[user] = generate_password_hash(CONF.admin.password) # Initialize the client factory and create # The correct client object ready for use @@ -466,7 +464,7 @@ def webchat(ctx, flush, port): packets.WatchList() packets.SeenList() - (socketio, app) = init_flask(loglevel, quiet) + socketio = init_flask(loglevel, quiet) rx_thread = rx.APRSDPluginRXThread( packet_queue=threads.packet_queue, ) @@ -482,7 +480,7 @@ def webchat(ctx, flush, port): keepalive.start() LOG.info("Start socketio.run()") socketio.run( - app, + flask_app, ssl_context="adhoc", host=CONF.admin.web_ip, port=port, diff --git a/aprsd/wsgi.py b/aprsd/wsgi.py index c68511b..17ffbe3 100644 --- a/aprsd/wsgi.py +++ b/aprsd/wsgi.py @@ -187,6 +187,7 @@ def index(): plugin_count=plugin_count, ) + @auth.login_required def messages(): track = packets.PacketTrack() @@ -197,9 +198,10 @@ def messages(): return flask.render_template("messages.html", messages=json.dumps(msgs)) + @auth.login_required @app.route("/packets") -def packets(): +def get_packets(): LOG.debug("/packets called") packet_list = aprsd_rpc_client.RPCClient().get_packet_list() if packet_list: @@ -212,6 +214,7 @@ def packets(): else: return json.dumps([]) + @auth.login_required @app.route("/plugins") def plugins(): @@ -221,6 +224,7 @@ def plugins(): return "reloaded" + @auth.login_required @app.route("/save") def save(): @@ -230,7 +234,6 @@ def save(): return json.dumps({"messages": "saved"}) - class LogUpdateThread(threads.APRSDThread): def __init__(self): diff --git a/tests/cmds/test_webchat.py b/tests/cmds/test_webchat.py index 3a0103d..53deb0d 100644 --- a/tests/cmds/test_webchat.py +++ b/tests/cmds/test_webchat.py @@ -39,9 +39,9 @@ class TestSendMessageCommand(unittest.TestCase): CliRunner() self.config_and_init() - socketio, flask_app = webchat.init_flask("DEBUG", False) + socketio = webchat.init_flask("DEBUG", False) self.assertIsInstance(socketio, flask_socketio.SocketIO) - self.assertIsInstance(flask_app, flask.Flask) + self.assertIsInstance(webchat.flask_app, flask.Flask) @mock.patch("aprsd.packets.tracker.PacketTrack.remove") @mock.patch("aprsd.cmds.webchat.socketio")