mirror of https://github.com/craigerl/aprsd.git
Removed flask-classful from webchat
This patch removed the dependency on flask-classful. This required making all of the flask web routing non class based. This patch also changes the aprsis class to allow retries for failed connections when the aprsis servers are full and not responding to login requests.
This commit is contained in:
parent
e1183a7e30
commit
6a6e854caf
|
@ -152,9 +152,10 @@ class APRSISClient(Client):
|
||||||
except LoginError as e:
|
except LoginError as e:
|
||||||
LOG.error(f"Failed to login to APRS-IS Server '{e}'")
|
LOG.error(f"Failed to login to APRS-IS Server '{e}'")
|
||||||
connected = False
|
connected = False
|
||||||
raise e
|
time.sleep(backoff)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
||||||
|
connected = False
|
||||||
time.sleep(backoff)
|
time.sleep(backoff)
|
||||||
backoff = backoff * 2
|
backoff = backoff * 2
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -112,23 +112,23 @@ class Aprsdis(aprslib.IS):
|
||||||
self._sendall(login_str)
|
self._sendall(login_str)
|
||||||
self.sock.settimeout(5)
|
self.sock.settimeout(5)
|
||||||
test = self.sock.recv(len(login_str) + 100)
|
test = self.sock.recv(len(login_str) + 100)
|
||||||
|
self.logger.debug("Server: '%s'", test)
|
||||||
if is_py3:
|
if is_py3:
|
||||||
test = test.decode("latin-1")
|
test = test.decode("latin-1")
|
||||||
test = test.rstrip()
|
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(",")
|
s = e.split(",")
|
||||||
if len(s):
|
if len(s):
|
||||||
server_string = s[0].replace("server ", "")
|
server_string = s[0].replace("server ", "")
|
||||||
else:
|
else:
|
||||||
server_string = e.replace("server ", "")
|
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 == "":
|
if callsign == "":
|
||||||
raise LoginError("Server responded with empty callsign???")
|
raise LoginError("Server responded with empty callsign???")
|
||||||
if callsign != self.callsign:
|
if callsign != self.callsign:
|
||||||
|
@ -141,6 +141,10 @@ class Aprsdis(aprslib.IS):
|
||||||
else:
|
else:
|
||||||
self.logger.info("Login successful")
|
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:
|
except LoginError as e:
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
self.close()
|
self.close()
|
||||||
|
@ -148,6 +152,7 @@ class Aprsdis(aprslib.IS):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.close()
|
self.close()
|
||||||
self.logger.error(f"Failed to login '{e}'")
|
self.logger.error(f"Failed to login '{e}'")
|
||||||
|
self.logger.exception(e)
|
||||||
raise LoginError("Failed to login")
|
raise LoginError("Failed to login")
|
||||||
|
|
||||||
def consumer(self, callback, blocking=True, immortal=False, raw=False):
|
def consumer(self, callback, blocking=True, immortal=False, raw=False):
|
||||||
|
|
|
@ -12,7 +12,6 @@ import click
|
||||||
import flask
|
import flask
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask.logging import default_handler
|
from flask.logging import default_handler
|
||||||
import flask_classful
|
|
||||||
from flask_httpauth import HTTPBasicAuth
|
from flask_httpauth import HTTPBasicAuth
|
||||||
from flask_socketio import Namespace, SocketIO
|
from flask_socketio import Namespace, SocketIO
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
@ -31,9 +30,16 @@ from aprsd.utils import objectstore, trace
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
auth = HTTPBasicAuth()
|
auth = HTTPBasicAuth()
|
||||||
users = None
|
users = {}
|
||||||
socketio = None
|
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):
|
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):
|
def _get_transport(stats):
|
||||||
if CONF.aprs_network.enabled:
|
if CONF.aprs_network.enabled:
|
||||||
transport = "aprs-is"
|
transport = "aprs-is"
|
||||||
aprs_connection = (
|
aprs_connection = (
|
||||||
"APRS-IS Server: <a href='http://status.aprs2.net' >"
|
"APRS-IS Server: <a href='http://status.aprs2.net' >"
|
||||||
"{}</a>".format(stats["stats"]["aprs-is"]["server"])
|
"{}</a>".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__,
|
|
||||||
)
|
)
|
||||||
|
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
|
return transport, aprs_connection
|
||||||
def send_message_status(self):
|
|
||||||
LOG.debug(request)
|
|
||||||
msgs = SentMessages()
|
|
||||||
info = msgs.get_all()
|
|
||||||
return json.dumps(info)
|
|
||||||
|
|
||||||
def _stats(self):
|
|
||||||
stats_obj = stats.APRSDStats()
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
|
|
||||||
time_format = "%m-%d-%Y %H:%M:%S"
|
@auth.login_required
|
||||||
stats_dict = stats_obj.stats()
|
@flask_app.route("/")
|
||||||
# Webchat doesnt need these
|
def index():
|
||||||
del stats_dict["aprsd"]["watch_list"]
|
ua_str = request.headers.get("User-Agent")
|
||||||
del stats_dict["aprsd"]["seen_list"]
|
# this takes about 2 seconds :(
|
||||||
# del stats_dict["email"]
|
user_agent = ua_parse(ua_str)
|
||||||
# del stats_dict["plugins"]
|
LOG.debug(f"Is mobile? {user_agent.is_mobile}")
|
||||||
# del stats_dict["messages"]
|
stats = _stats()
|
||||||
|
|
||||||
result = {
|
if user_agent.is_mobile:
|
||||||
"time": now.strftime(time_format),
|
html_template = "mobile.html"
|
||||||
"stats": stats_dict,
|
else:
|
||||||
}
|
html_template = "index.html"
|
||||||
|
|
||||||
return result
|
# For development
|
||||||
|
# html_template = "mobile.html"
|
||||||
|
|
||||||
def stats(self):
|
LOG.debug(f"Template {html_template}")
|
||||||
return json.dumps(self._stats())
|
|
||||||
|
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):
|
class SendMessageNamespace(Namespace):
|
||||||
|
@ -377,21 +385,9 @@ def setup_logging(flask_app, loglevel, quiet):
|
||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def init_flask(loglevel, quiet):
|
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)
|
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(
|
socketio = SocketIO(
|
||||||
flask_app, logger=False, engineio_logger=False,
|
flask_app, logger=False, engineio_logger=False,
|
||||||
|
@ -407,7 +403,7 @@ def init_flask(loglevel, quiet):
|
||||||
"/sendmsg",
|
"/sendmsg",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return socketio, flask_app
|
return socketio
|
||||||
|
|
||||||
|
|
||||||
# main() ###
|
# main() ###
|
||||||
|
@ -448,6 +444,8 @@ def webchat(ctx, flush, port):
|
||||||
LOG.info(f"APRSD Started version: {aprsd.__version__}")
|
LOG.info(f"APRSD Started version: {aprsd.__version__}")
|
||||||
|
|
||||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
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
|
# Initialize the client factory and create
|
||||||
# The correct client object ready for use
|
# The correct client object ready for use
|
||||||
|
@ -466,7 +464,7 @@ def webchat(ctx, flush, port):
|
||||||
packets.WatchList()
|
packets.WatchList()
|
||||||
packets.SeenList()
|
packets.SeenList()
|
||||||
|
|
||||||
(socketio, app) = init_flask(loglevel, quiet)
|
socketio = init_flask(loglevel, quiet)
|
||||||
rx_thread = rx.APRSDPluginRXThread(
|
rx_thread = rx.APRSDPluginRXThread(
|
||||||
packet_queue=threads.packet_queue,
|
packet_queue=threads.packet_queue,
|
||||||
)
|
)
|
||||||
|
@ -482,7 +480,7 @@ def webchat(ctx, flush, port):
|
||||||
keepalive.start()
|
keepalive.start()
|
||||||
LOG.info("Start socketio.run()")
|
LOG.info("Start socketio.run()")
|
||||||
socketio.run(
|
socketio.run(
|
||||||
app,
|
flask_app,
|
||||||
ssl_context="adhoc",
|
ssl_context="adhoc",
|
||||||
host=CONF.admin.web_ip,
|
host=CONF.admin.web_ip,
|
||||||
port=port,
|
port=port,
|
||||||
|
|
|
@ -187,6 +187,7 @@ def index():
|
||||||
plugin_count=plugin_count,
|
plugin_count=plugin_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def messages():
|
def messages():
|
||||||
track = packets.PacketTrack()
|
track = packets.PacketTrack()
|
||||||
|
@ -197,9 +198,10 @@ def messages():
|
||||||
|
|
||||||
return flask.render_template("messages.html", messages=json.dumps(msgs))
|
return flask.render_template("messages.html", messages=json.dumps(msgs))
|
||||||
|
|
||||||
|
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
@app.route("/packets")
|
@app.route("/packets")
|
||||||
def packets():
|
def get_packets():
|
||||||
LOG.debug("/packets called")
|
LOG.debug("/packets called")
|
||||||
packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
|
packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
|
||||||
if packet_list:
|
if packet_list:
|
||||||
|
@ -212,6 +214,7 @@ def packets():
|
||||||
else:
|
else:
|
||||||
return json.dumps([])
|
return json.dumps([])
|
||||||
|
|
||||||
|
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
@app.route("/plugins")
|
@app.route("/plugins")
|
||||||
def plugins():
|
def plugins():
|
||||||
|
@ -221,6 +224,7 @@ def plugins():
|
||||||
|
|
||||||
return "reloaded"
|
return "reloaded"
|
||||||
|
|
||||||
|
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
@app.route("/save")
|
@app.route("/save")
|
||||||
def save():
|
def save():
|
||||||
|
@ -230,7 +234,6 @@ def save():
|
||||||
return json.dumps({"messages": "saved"})
|
return json.dumps({"messages": "saved"})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LogUpdateThread(threads.APRSDThread):
|
class LogUpdateThread(threads.APRSDThread):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -39,9 +39,9 @@ class TestSendMessageCommand(unittest.TestCase):
|
||||||
CliRunner()
|
CliRunner()
|
||||||
self.config_and_init()
|
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(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.packets.tracker.PacketTrack.remove")
|
||||||
@mock.patch("aprsd.cmds.webchat.socketio")
|
@mock.patch("aprsd.cmds.webchat.socketio")
|
||||||
|
|
Loading…
Reference in New Issue