mirror of
https://github.com/craigerl/aprsd.git
synced 2026-08-17 17:13:49 -04:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7292744a78 | |||
| 619b1b708e | |||
| 008b2ab09e | |||
| 4b56e99689 | |||
| 10bf04929e | |||
| a9e8050ae6 | |||
| 82f77b7a6a | |||
| 570fdb98a7 | |||
| 9582812041 | |||
| 859f904602 | |||
| 34311f0fbd | |||
| 2416f0ea1a | |||
| 377842c2ec | |||
| a8dd9ce012 | |||
| 1d6a667987 | |||
| 2e9a204c74 | |||
| f922b3f97b | |||
| 8dd3b05bb1 |
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||
python-version: ["3.9", "3.10", "3.11"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
|
||||
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.8", "3.9", "3.10"]
|
||||
python-version: ["3.9", "3.10", "3.11"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
|
||||
@@ -6,8 +6,7 @@ on:
|
||||
aprsd_version:
|
||||
required: true
|
||||
options:
|
||||
- 2.5.9
|
||||
- 2.6.0
|
||||
- 3.0.0
|
||||
logLevel:
|
||||
description: 'Log level'
|
||||
required: true
|
||||
|
||||
@@ -5,7 +5,6 @@ repos:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: detect-private-key
|
||||
- id: check-merge-conflict
|
||||
- id: check-case-conflict
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
* Removed admin\_web.py
|
||||
* Removed some RPC server log noise
|
||||
* Fixed admin page packet date
|
||||
* RPC Server logs the client IP on failed auth
|
||||
* Start keepalive thread first
|
||||
* fixed an issue in the mobile webchat
|
||||
* Added dupe checkig code to webchat mobile
|
||||
* click on the div after added
|
||||
* Webchat suppress to display of dupe messages
|
||||
* Convert webchat internet urls to local static urls
|
||||
* Make use of webchat gps config options
|
||||
* Added new webchat config section
|
||||
* fixed webchat logging.logformat typeoh
|
||||
|
||||
v3.1.3
|
||||
------
|
||||
|
||||
* prep for 3.1.3
|
||||
* Forcefully allow development webchat flask
|
||||
|
||||
v3.1.2
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import time
|
||||
|
||||
import flask
|
||||
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
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
import aprsd
|
||||
from aprsd import cli_helper, client, conf, packets, plugin, threads
|
||||
from aprsd.log import rich as aprsd_logging
|
||||
from aprsd.rpc import client as aprsd_rpc_client
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
auth = HTTPBasicAuth()
|
||||
users = None
|
||||
app = None
|
||||
|
||||
|
||||
# HTTPBasicAuth doesn't work on a class method.
|
||||
# This has to be out here. Rely on the APRSDFlask
|
||||
# class to initialize the users from the config
|
||||
@auth.verify_password
|
||||
def verify_password(username, password):
|
||||
global users
|
||||
|
||||
if username in users and check_password_hash(users.get(username), password):
|
||||
return username
|
||||
|
||||
|
||||
class APRSDFlask(flask_classful.FlaskView):
|
||||
|
||||
def set_config(self):
|
||||
global users
|
||||
self.users = {}
|
||||
user = CONF.admin.user
|
||||
self.users[user] = generate_password_hash(CONF.admin.password)
|
||||
users = self.users
|
||||
|
||||
@auth.login_required
|
||||
def index(self):
|
||||
stats = self._stats()
|
||||
print(stats)
|
||||
LOG.debug(
|
||||
"watch list? {}".format(
|
||||
CONF.watch_list.callsigns,
|
||||
),
|
||||
)
|
||||
wl = aprsd_rpc_client.RPCClient().get_watch_list()
|
||||
if wl and wl.is_enabled():
|
||||
watch_count = len(wl)
|
||||
watch_age = wl.max_delta()
|
||||
else:
|
||||
watch_count = 0
|
||||
watch_age = 0
|
||||
|
||||
sl = aprsd_rpc_client.RPCClient().get_seen_list()
|
||||
if sl:
|
||||
seen_count = len(sl)
|
||||
else:
|
||||
seen_count = 0
|
||||
|
||||
pm = plugin.PluginManager()
|
||||
plugins = pm.get_plugins()
|
||||
plugin_count = len(plugins)
|
||||
|
||||
if CONF.aprs_network.enabled:
|
||||
transport = "aprs-is"
|
||||
aprs_connection = (
|
||||
"APRS-IS Server: <a href='http://status.aprs2.net' >"
|
||||
"{}</a>".format(stats["stats"]["aprs-is"]["server"])
|
||||
)
|
||||
else:
|
||||
# We might be connected to a KISS socket?
|
||||
if client.KISSClient.kiss_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:
|
||||
aprs_connection = (
|
||||
"SerialKISS://{}@{} baud".format(
|
||||
CONF.kiss_serial.device,
|
||||
CONF.kiss_serial.baudrate,
|
||||
)
|
||||
)
|
||||
|
||||
stats["transport"] = transport
|
||||
stats["aprs_connection"] = aprs_connection
|
||||
entries = conf.conf_to_dict()
|
||||
|
||||
return flask.render_template(
|
||||
"index.html",
|
||||
initial_stats=stats,
|
||||
aprs_connection=aprs_connection,
|
||||
callsign=CONF.callsign,
|
||||
version=aprsd.__version__,
|
||||
config_json=json.dumps(
|
||||
entries, indent=4,
|
||||
sort_keys=True, default=str,
|
||||
),
|
||||
watch_count=watch_count,
|
||||
watch_age=watch_age,
|
||||
seen_count=seen_count,
|
||||
plugin_count=plugin_count,
|
||||
)
|
||||
|
||||
@auth.login_required
|
||||
def messages(self):
|
||||
track = packets.PacketTrack()
|
||||
msgs = []
|
||||
for id in track:
|
||||
LOG.info(track[id].dict())
|
||||
msgs.append(track[id].dict())
|
||||
|
||||
return flask.render_template("messages.html", messages=json.dumps(msgs))
|
||||
|
||||
@auth.login_required
|
||||
def packets(self):
|
||||
packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
|
||||
if packet_list:
|
||||
packets = packet_list.get()
|
||||
tmp_list = []
|
||||
for pkt in packets:
|
||||
tmp_list.append(pkt.json)
|
||||
|
||||
return json.dumps(tmp_list)
|
||||
else:
|
||||
return json.dumps([])
|
||||
|
||||
@auth.login_required
|
||||
def plugins(self):
|
||||
pm = plugin.PluginManager()
|
||||
pm.reload_plugins()
|
||||
|
||||
return "reloaded"
|
||||
|
||||
@auth.login_required
|
||||
def save(self):
|
||||
"""Save the existing queue to disk."""
|
||||
track = packets.PacketTrack()
|
||||
track.save()
|
||||
return json.dumps({"messages": "saved"})
|
||||
|
||||
def _stats(self):
|
||||
track = aprsd_rpc_client.RPCClient().get_packet_track()
|
||||
now = datetime.datetime.now()
|
||||
|
||||
time_format = "%m-%d-%Y %H:%M:%S"
|
||||
|
||||
stats_dict = aprsd_rpc_client.RPCClient().get_stats_dict()
|
||||
if not stats_dict:
|
||||
stats_dict = {
|
||||
"aprsd": {},
|
||||
"aprs-is": {"server": ""},
|
||||
"messages": {
|
||||
"sent": 0,
|
||||
"received": 0,
|
||||
},
|
||||
"email": {
|
||||
"sent": 0,
|
||||
"received": 0,
|
||||
},
|
||||
"seen_list": {
|
||||
"sent": 0,
|
||||
"received": 0,
|
||||
},
|
||||
}
|
||||
|
||||
# Convert the watch_list entries to age
|
||||
wl = aprsd_rpc_client.RPCClient().get_watch_list()
|
||||
new_list = {}
|
||||
if wl:
|
||||
for call in wl.get_all():
|
||||
# call_date = datetime.datetime.strptime(
|
||||
# str(wl.last_seen(call)),
|
||||
# "%Y-%m-%d %H:%M:%S.%f",
|
||||
# )
|
||||
|
||||
# We have to convert the RingBuffer to a real list
|
||||
# so that json.dumps works.
|
||||
# pkts = []
|
||||
# for pkt in wl.get(call)["packets"].get():
|
||||
# pkts.append(pkt)
|
||||
|
||||
new_list[call] = {
|
||||
"last": wl.age(call),
|
||||
# "packets": pkts
|
||||
}
|
||||
|
||||
stats_dict["aprsd"]["watch_list"] = new_list
|
||||
packet_list = aprsd_rpc_client.RPCClient().get_packet_list()
|
||||
rx = tx = 0
|
||||
if packet_list:
|
||||
rx = packet_list.total_rx()
|
||||
tx = packet_list.total_tx()
|
||||
stats_dict["packets"] = {
|
||||
"sent": tx,
|
||||
"received": rx,
|
||||
}
|
||||
if track:
|
||||
size_tracker = len(track)
|
||||
else:
|
||||
size_tracker = 0
|
||||
|
||||
result = {
|
||||
"time": now.strftime(time_format),
|
||||
"size_tracker": size_tracker,
|
||||
"stats": stats_dict,
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def stats(self):
|
||||
return json.dumps(self._stats())
|
||||
|
||||
|
||||
class LogUpdateThread(threads.APRSDThread):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("LogUpdate")
|
||||
|
||||
def loop(self):
|
||||
global socketio
|
||||
|
||||
if socketio:
|
||||
log_entries = aprsd_rpc_client.RPCClient().get_log_entries()
|
||||
|
||||
if log_entries:
|
||||
for entry in log_entries:
|
||||
socketio.emit(
|
||||
"log_entry", entry,
|
||||
namespace="/logs",
|
||||
)
|
||||
|
||||
time.sleep(5)
|
||||
return True
|
||||
|
||||
|
||||
class LoggingNamespace(Namespace):
|
||||
log_thread = None
|
||||
|
||||
def on_connect(self):
|
||||
global socketio
|
||||
socketio.emit(
|
||||
"connected", {"data": "/logs Connected"},
|
||||
namespace="/logs",
|
||||
)
|
||||
self.log_thread = LogUpdateThread()
|
||||
self.log_thread.start()
|
||||
|
||||
def on_disconnect(self):
|
||||
LOG.debug("LOG Disconnected")
|
||||
if self.log_thread:
|
||||
self.log_thread.stop()
|
||||
|
||||
|
||||
def setup_logging(flask_app, loglevel, quiet):
|
||||
flask_log = logging.getLogger("werkzeug")
|
||||
flask_app.logger.removeHandler(default_handler)
|
||||
flask_log.removeHandler(default_handler)
|
||||
|
||||
log_level = conf.log.LOG_LEVELS[loglevel]
|
||||
flask_log.setLevel(log_level)
|
||||
date_format = CONF.logging.date_format
|
||||
flask_log.disabled = True
|
||||
flask_app.logger.disabled = True
|
||||
|
||||
if CONF.logging.rich_logging:
|
||||
log_format = "%(message)s"
|
||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||
rh = aprsd_logging.APRSDRichHandler(
|
||||
show_thread=True, thread_width=15,
|
||||
rich_tracebacks=True, omit_repeated_times=False,
|
||||
)
|
||||
rh.setFormatter(log_formatter)
|
||||
flask_log.addHandler(rh)
|
||||
|
||||
log_file = CONF.logging.logfile
|
||||
|
||||
if log_file:
|
||||
log_format = CONF.logging.logformat
|
||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||
fh = RotatingFileHandler(
|
||||
log_file, maxBytes=(10248576 * 5),
|
||||
backupCount=4,
|
||||
)
|
||||
fh.setFormatter(log_formatter)
|
||||
flask_log.addHandler(fh)
|
||||
|
||||
|
||||
def init_flask(loglevel, quiet):
|
||||
global socketio
|
||||
|
||||
flask_app = flask.Flask(
|
||||
"aprsd",
|
||||
static_url_path="/static",
|
||||
static_folder="web/admin/static",
|
||||
template_folder="web/admin/templates",
|
||||
)
|
||||
setup_logging(flask_app, loglevel, quiet)
|
||||
server = APRSDFlask()
|
||||
server.set_config()
|
||||
flask_app.route("/", methods=["GET"])(server.index)
|
||||
flask_app.route("/stats", methods=["GET"])(server.stats)
|
||||
flask_app.route("/messages", methods=["GET"])(server.messages)
|
||||
flask_app.route("/packets", methods=["GET"])(server.packets)
|
||||
flask_app.route("/save", methods=["GET"])(server.save)
|
||||
flask_app.route("/plugins", methods=["GET"])(server.plugins)
|
||||
|
||||
socketio = SocketIO(
|
||||
flask_app, logger=False, engineio_logger=False,
|
||||
# async_mode="threading",
|
||||
)
|
||||
# import eventlet
|
||||
# eventlet.monkey_patch()
|
||||
gunicorn_logger = logging.getLogger("gunicorn.error")
|
||||
flask_app.logger.handlers = gunicorn_logger.handlers
|
||||
flask_app.logger.setLevel(gunicorn_logger.level)
|
||||
|
||||
socketio.on_namespace(LoggingNamespace("/logs"))
|
||||
return socketio, flask_app
|
||||
|
||||
|
||||
def create_app(config_file=None, log_level=None):
|
||||
global socketio
|
||||
global app
|
||||
|
||||
default_config_file = cli_helper.DEFAULT_CONFIG_FILE
|
||||
if not config_file:
|
||||
config_file = default_config_file
|
||||
|
||||
CONF(
|
||||
[], project="aprsd", version=aprsd.__version__,
|
||||
default_config_files=[config_file],
|
||||
)
|
||||
|
||||
if not log_level:
|
||||
log_level = CONF.logging.log_level
|
||||
|
||||
socketio, app = init_flask(log_level, False)
|
||||
setup_logging(app, log_level, False)
|
||||
return app
|
||||
|
||||
|
||||
if __name__ == "aprsd.flask":
|
||||
sio, app = create_app()
|
||||
+6
-5
@@ -24,7 +24,7 @@ TRANSPORT_SERIALKISS = "serialkiss"
|
||||
factory = None
|
||||
|
||||
|
||||
class Client:
|
||||
class Client(metaclass=trace.TraceWrapperMetaclass):
|
||||
"""Singleton client class that constructs the aprslib connection."""
|
||||
|
||||
_instance = None
|
||||
@@ -67,6 +67,9 @@ class Client:
|
||||
else:
|
||||
LOG.warning("Client not initialized, nothing to reset.")
|
||||
|
||||
# Recreate the client
|
||||
LOG.info(f"Creating new client {self.client}")
|
||||
|
||||
@abc.abstractmethod
|
||||
def setup_connection(self):
|
||||
pass
|
||||
@@ -86,7 +89,7 @@ class Client:
|
||||
pass
|
||||
|
||||
|
||||
class APRSISClient(Client):
|
||||
class APRSISClient(Client, metaclass=trace.TraceWrapperMetaclass):
|
||||
|
||||
_client = None
|
||||
|
||||
@@ -135,7 +138,6 @@ class APRSISClient(Client):
|
||||
"""APRS lib already decodes this."""
|
||||
return core.Packet.factory(args[0])
|
||||
|
||||
@trace.trace
|
||||
def setup_connection(self):
|
||||
user = CONF.aprs_network.login
|
||||
password = CONF.aprs_network.password
|
||||
@@ -172,7 +174,7 @@ class APRSISClient(Client):
|
||||
return aprs_client
|
||||
|
||||
|
||||
class KISSClient(Client):
|
||||
class KISSClient(Client, metaclass=trace.TraceWrapperMetaclass):
|
||||
|
||||
_client = None
|
||||
|
||||
@@ -241,7 +243,6 @@ class KISSClient(Client):
|
||||
else:
|
||||
return packet
|
||||
|
||||
@trace.trace
|
||||
def setup_connection(self):
|
||||
self._client = kiss.KISS3Client()
|
||||
return self._client
|
||||
|
||||
@@ -94,6 +94,9 @@ def server(ctx, flush):
|
||||
packets.WatchList().load()
|
||||
packets.SeenList().load()
|
||||
|
||||
keepalive = threads.KeepAliveThread()
|
||||
keepalive.start()
|
||||
|
||||
rx_thread = rx.APRSDPluginRXThread(
|
||||
packet_queue=threads.packet_queue,
|
||||
)
|
||||
@@ -105,9 +108,6 @@ def server(ctx, flush):
|
||||
|
||||
packets.PacketTrack().restart()
|
||||
|
||||
keepalive = threads.KeepAliveThread()
|
||||
keepalive.start()
|
||||
|
||||
if CONF.rpc_settings.enabled:
|
||||
rpc = rpc_server.APRSDRPCThread()
|
||||
rpc.start()
|
||||
|
||||
+20
-8
@@ -163,7 +163,7 @@ class WebChatProcessPacketThread(rx.APRSDProcessPacketThread):
|
||||
|
||||
message = packet.get("message_text", None)
|
||||
msg = {
|
||||
"id": 0,
|
||||
"id": packet.msgNo,
|
||||
"ts": packet.get("timestamp", time.time()),
|
||||
"ack": False,
|
||||
"from": fromcall,
|
||||
@@ -239,6 +239,13 @@ def index():
|
||||
stats["transport"] = transport
|
||||
stats["aprs_connection"] = aprs_connection
|
||||
LOG.debug(f"initial stats = {stats}")
|
||||
latitude = CONF.webchat.latitude
|
||||
if latitude:
|
||||
latitude = float(CONF.webchat.latitude)
|
||||
|
||||
longitude = CONF.webchat.longitude
|
||||
if longitude:
|
||||
longitude = float(longitude)
|
||||
|
||||
return flask.render_template(
|
||||
html_template,
|
||||
@@ -246,6 +253,8 @@ def index():
|
||||
aprs_connection=aprs_connection,
|
||||
callsign=CONF.callsign,
|
||||
version=aprsd.__version__,
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
)
|
||||
|
||||
|
||||
@@ -377,7 +386,7 @@ def setup_logging(flask_app, loglevel, quiet):
|
||||
log_file = CONF.logging.logfile
|
||||
|
||||
if log_file:
|
||||
log_format = CONF.loging.logformat
|
||||
log_format = CONF.logging.logformat
|
||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||
fh = RotatingFileHandler(
|
||||
log_file, maxBytes=(10248576 * 5),
|
||||
@@ -427,8 +436,8 @@ def init_flask(loglevel, quiet):
|
||||
"--port",
|
||||
"port",
|
||||
show_default=True,
|
||||
default=80,
|
||||
help="Port to listen to web requests",
|
||||
default=None,
|
||||
help="Port to listen to web requests. This overrides the config.webchat.web_port setting.",
|
||||
)
|
||||
@click.pass_context
|
||||
@cli_helper.process_standard_options
|
||||
@@ -450,6 +459,8 @@ def webchat(ctx, flush, port):
|
||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||
user = CONF.admin.user
|
||||
users[user] = generate_password_hash(CONF.admin.password)
|
||||
if not port:
|
||||
port = CONF.webchat.web_port
|
||||
|
||||
# Initialize the client factory and create
|
||||
# The correct client object ready for use
|
||||
@@ -468,6 +479,10 @@ def webchat(ctx, flush, port):
|
||||
packets.WatchList()
|
||||
packets.SeenList()
|
||||
|
||||
keepalive = threads.KeepAliveThread()
|
||||
LOG.info("Start KeepAliveThread")
|
||||
keepalive.start()
|
||||
|
||||
socketio = init_flask(loglevel, quiet)
|
||||
rx_thread = rx.APRSDPluginRXThread(
|
||||
packet_queue=threads.packet_queue,
|
||||
@@ -479,16 +494,13 @@ def webchat(ctx, flush, port):
|
||||
)
|
||||
process_thread.start()
|
||||
|
||||
keepalive = threads.KeepAliveThread()
|
||||
LOG.info("Start KeepAliveThread")
|
||||
keepalive.start()
|
||||
LOG.info("Start socketio.run()")
|
||||
socketio.run(
|
||||
flask_app,
|
||||
# This is broken for now after removing cryptography
|
||||
# and pyopenssl
|
||||
# ssl_context="adhoc",
|
||||
host=CONF.admin.web_ip,
|
||||
host=CONF.webchat.web_ip,
|
||||
port=port,
|
||||
allow_unsafe_werkzeug=True,
|
||||
)
|
||||
|
||||
@@ -19,6 +19,10 @@ rpc_group = cfg.OptGroup(
|
||||
name="rpc_settings",
|
||||
title="RPC Settings for admin <--> web",
|
||||
)
|
||||
webchat_group = cfg.OptGroup(
|
||||
name="webchat",
|
||||
title="Settings specific to the webchat command",
|
||||
)
|
||||
|
||||
|
||||
aprsd_opts = [
|
||||
@@ -163,6 +167,29 @@ enabled_plugins_opts = [
|
||||
),
|
||||
]
|
||||
|
||||
webchat_opts = [
|
||||
cfg.IPOpt(
|
||||
"web_ip",
|
||||
default="0.0.0.0",
|
||||
help="The ip address to listen on",
|
||||
),
|
||||
cfg.PortOpt(
|
||||
"web_port",
|
||||
default=8001,
|
||||
help="The port to listen on",
|
||||
),
|
||||
cfg.StrOpt(
|
||||
"latitude",
|
||||
default=None,
|
||||
help="Latitude for the GPS Beacon button. If not set, the button will not be enabled.",
|
||||
),
|
||||
cfg.StrOpt(
|
||||
"longitude",
|
||||
default=None,
|
||||
help="Longitude for the GPS Beacon button. If not set, the button will not be enabled.",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def register_opts(config):
|
||||
config.register_opts(aprsd_opts)
|
||||
@@ -173,6 +200,8 @@ def register_opts(config):
|
||||
config.register_opts(watch_list_opts, group=watch_list_group)
|
||||
config.register_group(rpc_group)
|
||||
config.register_opts(rpc_opts, group=rpc_group)
|
||||
config.register_group(webchat_group)
|
||||
config.register_opts(webchat_opts, group=webchat_group)
|
||||
|
||||
|
||||
def list_opts():
|
||||
@@ -181,4 +210,5 @@ def list_opts():
|
||||
admin_group.name: admin_opts,
|
||||
watch_list_group.name: watch_list_opts,
|
||||
rpc_group.name: rpc_opts,
|
||||
webchat_group.name: webchat_opts,
|
||||
}
|
||||
|
||||
+7
-10
@@ -16,8 +16,13 @@ LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
def magic_word_authenticator(sock):
|
||||
client_ip = sock.getpeername()[0]
|
||||
magic = sock.recv(len(CONF.rpc_settings.magic_word)).decode()
|
||||
if magic != CONF.rpc_settings.magic_word:
|
||||
LOG.error(
|
||||
f"wrong magic word passed from {client_ip} "
|
||||
"'{magic}' != '{CONF.rpc_settings.magic_word}'",
|
||||
)
|
||||
raise AuthenticationError(
|
||||
f"wrong magic word passed in '{magic}'"
|
||||
f" != '{CONF.rpc_settings.magic_word}'",
|
||||
@@ -52,51 +57,43 @@ class APRSDService(rpyc.Service):
|
||||
def on_connect(self, conn):
|
||||
# code that runs when a connection is created
|
||||
# (to init the service, if needed)
|
||||
LOG.info("Connected")
|
||||
LOG.info("RPC Client Connected")
|
||||
self._conn = conn
|
||||
|
||||
def on_disconnect(self, conn):
|
||||
# code that runs after the connection has already closed
|
||||
# (to finalize the service, if needed)
|
||||
LOG.info("Disconnected")
|
||||
LOG.info("RPC Client Disconnected")
|
||||
self._conn = None
|
||||
|
||||
@rpyc.exposed
|
||||
def get_stats(self):
|
||||
LOG.info("get_stats")
|
||||
stat = stats.APRSDStats()
|
||||
stats_dict = stat.stats()
|
||||
return_str = json.dumps(stats_dict, indent=4, sort_keys=True, default=str)
|
||||
LOG.info(f"get_stats: return size {len(return_str)}")
|
||||
return return_str
|
||||
|
||||
@rpyc.exposed
|
||||
def get_stats_obj(self):
|
||||
LOG.info("get_stats_obj")
|
||||
return stats.APRSDStats()
|
||||
|
||||
@rpyc.exposed
|
||||
def get_packet_list(self):
|
||||
LOG.info("get_packet_list")
|
||||
return packets.PacketList()
|
||||
|
||||
@rpyc.exposed
|
||||
def get_packet_track(self):
|
||||
LOG.info("get_packet_track")
|
||||
return packets.PacketTrack()
|
||||
|
||||
@rpyc.exposed
|
||||
def get_watch_list(self):
|
||||
LOG.info("get_watch_list")
|
||||
return packets.WatchList()
|
||||
|
||||
@rpyc.exposed
|
||||
def get_seen_list(self):
|
||||
LOG.info("get_seen_list")
|
||||
return packets.SeenList()
|
||||
|
||||
@rpyc.exposed
|
||||
def get_log_entries(self):
|
||||
LOG.info("get_log_entries")
|
||||
entries = log_monitor.LogEntries().get_all_and_purge()
|
||||
return json.dumps(entries, default=str)
|
||||
|
||||
@@ -246,7 +246,6 @@ class APRSDStats:
|
||||
},
|
||||
"plugins": plugin_stats,
|
||||
}
|
||||
LOG.info("APRSD Stats: DONE")
|
||||
return stats
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -111,14 +111,14 @@ function update_packets( data ) {
|
||||
pkt = JSON.parse(val);
|
||||
|
||||
update_watchlist_from_packet(pkt['from_call'], pkt);
|
||||
if ( packet_list.hasOwnProperty(pkt.timestamp) == false ) {
|
||||
if ( packet_list.hasOwnProperty(pkt['timestamp']) == false ) {
|
||||
// Store the packet
|
||||
packet_list[pkt.timestamp] = pkt;
|
||||
packet_list[pkt['timestamp']] = pkt;
|
||||
//ts_str = val["timestamp"].toString();
|
||||
//ts = ts_str.split(".")[0]*1000;
|
||||
ts = pkt.timestamp
|
||||
var d = new Date(ts).toLocaleDateString("en-US");
|
||||
var t = new Date(ts).toLocaleTimeString("en-US");
|
||||
ts = pkt['timestamp'] * 1000;
|
||||
var d = new Date(ts).toLocaleDateString();
|
||||
var t = new Date(ts).toLocaleTimeString();
|
||||
var from_call = pkt.from_call;
|
||||
if (from_call == our_callsign) {
|
||||
title_id = 'title_tx';
|
||||
|
||||
+1311
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,17 @@
|
||||
|
||||
function init_gps() {
|
||||
console.log("init_gps Called.")
|
||||
console.log("latitude: "+latitude)
|
||||
console.log("longitude: "+longitude)
|
||||
$("#send_beacon").click(function() {
|
||||
console.log("Send a beacon!")
|
||||
getLocation();
|
||||
if (!isNaN(latitude) && !isNaN(longitude)) {
|
||||
// webchat admin has hard coded lat/long in the config file
|
||||
showPosition({'coords': {'latitude': latitude, 'longitude': longitude}})
|
||||
} else {
|
||||
// Try to get the current location from the browser
|
||||
getLocation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
var cleared = false;
|
||||
var callsign_list = {};
|
||||
var message_list = {};
|
||||
var from_msg_list = {};
|
||||
const socket = io("/sendmsg");
|
||||
|
||||
function size_dict(d){c=0; for (i in d) ++c; return c}
|
||||
|
||||
function init_chat() {
|
||||
const socket = io("/sendmsg");
|
||||
socket.on('connect', function () {
|
||||
console.log("Connected to socketio");
|
||||
});
|
||||
@@ -43,7 +44,6 @@ function init_chat() {
|
||||
}
|
||||
socket.emit("send", msg);
|
||||
$('#message').val('');
|
||||
$('#to_call').val('');
|
||||
});
|
||||
|
||||
init_gps();
|
||||
@@ -118,8 +118,9 @@ function append_message_html(callsign, msg_html, new_callsign) {
|
||||
$(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow");
|
||||
}
|
||||
|
||||
function create_message_html(time, from, to, message, ack) {
|
||||
msg_html = '<div class="item">';
|
||||
function create_message_html(time, from, to, message, ack, msg) {
|
||||
div_id = from + "_" + msg.id;
|
||||
msg_html = '<div class="item" id="'+div_id+'">';
|
||||
msg_html += '<div class="tiny text">'+time+'</div>';
|
||||
msg_html += '<div class="middle aligned content">';
|
||||
msg_html += '<div class="tiny red header">'+from+'</div>';
|
||||
@@ -136,6 +137,13 @@ function create_message_html(time, from, to, message, ack) {
|
||||
return msg_html
|
||||
}
|
||||
|
||||
function flash_message(msg) {
|
||||
// Callback function to bring a hidden box back
|
||||
id = msg.from + "_" + msg.id;
|
||||
var msgid = $('#'+id);
|
||||
msgid.effect("pulsate", { times:3 }, 2000);
|
||||
}
|
||||
|
||||
function sent_msg(msg) {
|
||||
var msgsdiv = $("#sendMsgsDiv");
|
||||
|
||||
@@ -147,12 +155,27 @@ function sent_msg(msg) {
|
||||
var d = new Date(ts).toLocaleDateString("en-US")
|
||||
var t = new Date(ts).toLocaleTimeString("en-US")
|
||||
|
||||
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id);
|
||||
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg);
|
||||
append_message(msg['to'], msg, msg_html);
|
||||
}
|
||||
|
||||
function from_msg(msg) {
|
||||
var msgsdiv = $("#sendMsgsDiv");
|
||||
console.log(msg);
|
||||
if (!from_msg_list.hasOwnProperty(msg.from)) {
|
||||
from_msg_list[msg.from] = new Array();
|
||||
}
|
||||
|
||||
if (msg.id in from_msg_list[msg.from]) {
|
||||
// We already have this message
|
||||
console.log("We already have this message " + msg);
|
||||
// Do some flashy thing?
|
||||
flash_message(msg);
|
||||
return false
|
||||
} else {
|
||||
console.log("Adding message " + msg.id + " to " + msg.from);
|
||||
from_msg_list[msg.from][msg.id] = msg
|
||||
}
|
||||
|
||||
// We have an existing entry
|
||||
ts_str = msg["ts"].toString();
|
||||
@@ -164,7 +187,7 @@ function from_msg(msg) {
|
||||
var t = new Date(ts).toLocaleTimeString("en-US")
|
||||
|
||||
from = msg['from']
|
||||
msg_html = create_message_html(t, from, false, msg['message'], false);
|
||||
msg_html = create_message_html(t, from, false, msg['message'], false, msg);
|
||||
append_message(from, msg, msg_html);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
var cleared = false;
|
||||
var callsign_list = {};
|
||||
var message_list = {};
|
||||
var from_msg_list = {};
|
||||
const socket = io("/sendmsg");
|
||||
|
||||
MSG_TYPE_TX = "tx";
|
||||
MSG_TYPE_RX = "rx";
|
||||
MSG_TYPE_ACK = "ack";
|
||||
|
||||
function size_dict(d){c=0; for (i in d) ++c; return c}
|
||||
|
||||
function init_chat() {
|
||||
@@ -15,24 +20,28 @@ function init_chat() {
|
||||
});
|
||||
|
||||
socket.on("sent", function(msg) {
|
||||
if (cleared == false) {
|
||||
if (cleared === false) {
|
||||
console.log("CLEARING #msgsTabsDiv");
|
||||
var msgsdiv = $("#msgsTabsDiv");
|
||||
msgsdiv.html('')
|
||||
cleared = true
|
||||
msgsdiv.html('');
|
||||
cleared = true;
|
||||
}
|
||||
msg["type"] = MSG_TYPE_TX;
|
||||
sent_msg(msg);
|
||||
});
|
||||
|
||||
socket.on("ack", function(msg) {
|
||||
update_msg(msg);
|
||||
msg["type"] = MSG_TYPE_ACK;
|
||||
ack_msg(msg);
|
||||
});
|
||||
|
||||
socket.on("new", function(msg) {
|
||||
if (cleared == false) {
|
||||
if (cleared === false) {
|
||||
var msgsdiv = $("#msgsTabsDiv");
|
||||
msgsdiv.html('')
|
||||
cleared = true
|
||||
cleared = true;
|
||||
}
|
||||
msg["type"] = MSG_TYPE_RX;
|
||||
from_msg(msg);
|
||||
});
|
||||
|
||||
@@ -46,32 +55,124 @@ function init_chat() {
|
||||
});
|
||||
|
||||
init_gps();
|
||||
// Try and load any existing chat threads from last time
|
||||
init_messages();
|
||||
}
|
||||
|
||||
function add_callsign(callsign) {
|
||||
/* Ensure a callsign exists in the left hand nav */
|
||||
function message_ts_id(msg) {
|
||||
//Create a 'id' from the message timestamp
|
||||
ts_str = msg["ts"].toString();
|
||||
ts = ts_str.split(".")[0]*1000;
|
||||
id = ts_str.split('.')[0];
|
||||
return {'timestamp': ts, 'id': id};
|
||||
}
|
||||
|
||||
if (callsign in callsign_list) {
|
||||
return false
|
||||
}
|
||||
function time_ack_from_msg(msg) {
|
||||
// Return the time and ack_id from a message
|
||||
ts_id = message_ts_id(msg);
|
||||
ts = ts_id['timestamp'];
|
||||
id = ts_id['id'];
|
||||
ack_id = "ack_" + id
|
||||
|
||||
var d = new Date(ts).toLocaleDateString("en-US")
|
||||
var t = new Date(ts).toLocaleTimeString("en-US")
|
||||
return {'time': t, 'date': d, 'ack_id': ack_id};
|
||||
}
|
||||
|
||||
function save_data() {
|
||||
// Save the relevant data to local storage
|
||||
localStorage.setItem('callsign_list', JSON.stringify(callsign_list));
|
||||
localStorage.setItem('message_list', JSON.stringify(message_list));
|
||||
}
|
||||
|
||||
function init_messages() {
|
||||
// This tries to load any previous conversations from local storage
|
||||
callsign_list = JSON.parse(localStorage.getItem('callsign_list'));
|
||||
message_list = JSON.parse(localStorage.getItem('message_list'));
|
||||
console.log("init_messages");
|
||||
if (callsign_list == null) {
|
||||
callsign_list = {};
|
||||
}
|
||||
if (message_list == null) {
|
||||
message_list = {};
|
||||
}
|
||||
console.log(callsign_list);
|
||||
console.log(message_list);
|
||||
|
||||
// Now loop through each callsign and add the tabs
|
||||
first_callsign = null;
|
||||
for (callsign in callsign_list) {
|
||||
console.log("Adding callsign " + callsign);
|
||||
if (first_callsign === null) {
|
||||
first_callsign = callsign;
|
||||
console.log("first_callsign " + first_callsign)
|
||||
}
|
||||
create_callsign_tab(callsign);
|
||||
}
|
||||
// and then populate the messages in order
|
||||
for (callsign in message_list) {
|
||||
new_callsign = true;
|
||||
cleared = true;
|
||||
for (id in message_list[callsign]) {
|
||||
msg = message_list[callsign][id];
|
||||
info = time_ack_from_msg(msg);
|
||||
t = info['time'];
|
||||
ack_id = false;
|
||||
acked = false;
|
||||
if (msg['type'] == MSG_TYPE_TX) {
|
||||
ack_id = info['ack_id'];
|
||||
acked = msg['ack'];
|
||||
}
|
||||
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg, acked);
|
||||
append_message_html(callsign, msg_html, new_callsign);
|
||||
new_callsign = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Click on the very first tab
|
||||
if (first_callsign !== null) {
|
||||
click_div = '#'+tab_string(first_callsign);
|
||||
var click_timer = setTimeout(function() {
|
||||
console.log("Click on first tab " + click_div);
|
||||
$(click_div).click();
|
||||
clearTimeout(click_timer);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
function create_callsign_tab(callsign) {
|
||||
//Create the html for the callsign tab and insert it into the DOM
|
||||
console.log("create_callsign_tab " + callsign)
|
||||
var callsignTabs = $("#callsignTabs");
|
||||
tab_name = tab_string(callsign);
|
||||
tab_content = tab_content_name(callsign);
|
||||
divname = content_divname(callsign);
|
||||
|
||||
item_html = '<div class="tablinks" id="'+tab_name+'" onclick="openCallsign(event, \''+callsign+'\');">'+callsign+'</div>';
|
||||
console.log(item_html);
|
||||
callsignTabs.append(item_html);
|
||||
}
|
||||
|
||||
function add_callsign(callsign) {
|
||||
/* Ensure a callsign exists in the left hand nav */
|
||||
if (callsign in callsign_list) {
|
||||
return false
|
||||
}
|
||||
create_callsign_tab(callsign);
|
||||
callsign_list[callsign] = true;
|
||||
return true
|
||||
}
|
||||
|
||||
function append_message(callsign, msg, msg_html) {
|
||||
console.log("append_message " + callsign + " " + msg + " " + msg_html);
|
||||
new_callsign = false
|
||||
if (!message_list.hasOwnProperty(callsign)) {
|
||||
message_list[callsign] = new Array();
|
||||
//message_list[callsign] = new Array();
|
||||
message_list[callsign] = {};
|
||||
}
|
||||
message_list[callsign].push(msg);
|
||||
ts_id = message_ts_id(msg);
|
||||
id = ts_id['id']
|
||||
message_list[callsign][id] = msg;
|
||||
|
||||
// Find the right div to place the html
|
||||
new_callsign = add_callsign(callsign);
|
||||
@@ -97,27 +198,43 @@ function content_divname(callsign) {
|
||||
|
||||
function append_message_html(callsign, msg_html, new_callsign) {
|
||||
var msgsTabs = $('#msgsTabsDiv');
|
||||
console.log("append_message_html " + callsign + " " + msg_html + " " + new_callsign);
|
||||
divname_str = tab_content_name(callsign);
|
||||
divname = content_divname(callsign);
|
||||
if (new_callsign) {
|
||||
// we have to add a new DIV
|
||||
console.log("new callsign, create msg_div_html ");
|
||||
msg_div_html = '<div class="tabcontent" id="'+divname_str+'" style="height:450px;">'+msg_html+'</div>';
|
||||
console.log(msg_div_html);
|
||||
msgsTabs.append(msg_div_html);
|
||||
} else {
|
||||
var msgDiv = $(divname);
|
||||
console.log("Appending ("+ msg_html + ") to " + divname);
|
||||
console.log(msgDiv);
|
||||
msgDiv.append(msg_html);
|
||||
console.log(msgDiv);
|
||||
}
|
||||
console.log("divname " + divname);
|
||||
console.log($(divname).length);
|
||||
|
||||
$(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow");
|
||||
if ($(divname).length > 0) {
|
||||
$(divname).animate({scrollTop: $(divname)[0].scrollHeight}, "slow");
|
||||
}
|
||||
$(divname).trigger('click');
|
||||
}
|
||||
|
||||
function create_message_html(time, from, to, message, ack) {
|
||||
msg_html = '<div class="item">';
|
||||
function create_message_html(time, from, to, message, ack_id, msg, acked=false) {
|
||||
div_id = from + "_" + msg.id;
|
||||
msg_html = '<div class="item" id="'+div_id+'">';
|
||||
msg_html += '<div class="tiny text">'+time+'</div>';
|
||||
msg_html += '<div class="middle aligned content">';
|
||||
msg_html += '<div class="tiny red header">'+from+'</div>';
|
||||
if (ack) {
|
||||
msg_html += '<i class="thumbs down outline icon" id="' + ack_id + '" data-content="Waiting for ACK"></i>';
|
||||
if (ack_id) {
|
||||
if (acked) {
|
||||
msg_html += '<div class="middle aligned content"><i class="thumbs up outline icon" id="' + ack_id + '" data-content="Message ACKed"></i></div>';
|
||||
} else {
|
||||
msg_html += '<div class="middle aligned content"><i class="thumbs down outline icon" id="' + ack_id + '" data-content="Waiting for ACK"></i></div>';
|
||||
}
|
||||
} else {
|
||||
msg_html += '<i class="phone volume icon" data-content="Recieved Message"></i>';
|
||||
}
|
||||
@@ -129,60 +246,90 @@ function create_message_html(time, from, to, message, ack) {
|
||||
return msg_html
|
||||
}
|
||||
|
||||
function flash_message(msg) {
|
||||
// Callback function to bring a hidden box back
|
||||
id = msg.from + "_" + msg.id;
|
||||
var msgid = $('#'+id);
|
||||
msgid.effect("pulsate", { times:3 }, 2000);
|
||||
}
|
||||
|
||||
|
||||
function sent_msg(msg) {
|
||||
var msgsdiv = $("#sendMsgsDiv");
|
||||
info = time_ack_from_msg(msg);
|
||||
t = info['time'];
|
||||
ack_id = info['ack_id'];
|
||||
|
||||
ts_str = msg["ts"].toString();
|
||||
ts = ts_str.split(".")[0]*1000;
|
||||
id = ts_str.split('.')[0]
|
||||
ack_id = "ack_" + id
|
||||
|
||||
var d = new Date(ts).toLocaleDateString("en-US")
|
||||
var t = new Date(ts).toLocaleTimeString("en-US")
|
||||
|
||||
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id);
|
||||
msg_html = create_message_html(t, msg['from'], msg['to'], msg['message'], ack_id, msg, false);
|
||||
append_message(msg['to'], msg, msg_html);
|
||||
save_data();
|
||||
}
|
||||
|
||||
function from_msg(msg) {
|
||||
var msgsdiv = $("#sendMsgsDiv");
|
||||
console.log(msg);
|
||||
if (!from_msg_list.hasOwnProperty(msg.from)) {
|
||||
from_msg_list[msg.from] = new Array();
|
||||
}
|
||||
|
||||
// We have an existing entry
|
||||
ts_str = msg["ts"].toString();
|
||||
ts = ts_str.split(".")[0]*1000;
|
||||
id = ts_str.split('.')[0]
|
||||
ack_id = "ack_" + id
|
||||
if (msg.id in from_msg_list[msg.from]) {
|
||||
// We already have this message
|
||||
console.log("We already have this message " + msg);
|
||||
// Do some flashy thing?
|
||||
flash_message(msg);
|
||||
return false
|
||||
} else {
|
||||
console.log("Adding message " + msg.id + " to " + msg.from);
|
||||
from_msg_list[msg.from][msg.id] = msg
|
||||
}
|
||||
|
||||
var d = new Date(ts).toLocaleDateString("en-US")
|
||||
var t = new Date(ts).toLocaleTimeString("en-US")
|
||||
info = time_ack_from_msg(msg);
|
||||
t = info['time'];
|
||||
ack_id = info['ack_id'];
|
||||
|
||||
from = msg['from']
|
||||
msg_html = create_message_html(t, from, false, msg['message'], false);
|
||||
msg_html = create_message_html(t, from, false, msg['message'], false, msg, false);
|
||||
append_message(from, msg, msg_html);
|
||||
save_data();
|
||||
}
|
||||
|
||||
function update_msg(msg) {
|
||||
var msgsdiv = $("#sendMsgsDiv");
|
||||
// We have an existing entry
|
||||
ts_str = msg["ts"].toString();
|
||||
id = ts_str.split('.')[0]
|
||||
pretty_id = "pretty_" + id
|
||||
loader_id = "loader_" + id
|
||||
ack_id = "ack_" + id
|
||||
span_id = "span_" + id
|
||||
function ack_msg(msg) {
|
||||
// Acknowledge a message
|
||||
console.log("ack_msg ");
|
||||
console.log(msg);
|
||||
console.log(message_list);
|
||||
|
||||
// We have an existing entry
|
||||
ts_id = message_ts_id(msg);
|
||||
console.log(ts_id)
|
||||
id = ts_id['id'];
|
||||
//Mark the message as acked
|
||||
callsign = msg['to'];
|
||||
// Ensure the message_list has this callsign
|
||||
if (!message_list.hasOwnProperty(callsign)) {
|
||||
console.log("No message_list for " + callsign);
|
||||
return false
|
||||
}
|
||||
// Ensure the message_list has this id
|
||||
if (!message_list[callsign].hasOwnProperty(id)) {
|
||||
console.log("No message_list for " + callsign + " " + id);
|
||||
return false
|
||||
}
|
||||
console.log("Marking message as acked " + callsign + " " + id)
|
||||
if (message_list[callsign][id]['ack'] == true) {
|
||||
console.log("Message already acked");
|
||||
return false;
|
||||
}
|
||||
message_list[callsign][id]['ack'] = true;
|
||||
ack_id = "ack_" + id
|
||||
|
||||
if (msg['ack'] == true) {
|
||||
var ack_div = $('#' + ack_id);
|
||||
//ack_div.removeClass('thumbs up outline icon');
|
||||
ack_div.removeClass('thumbs down outline icon');
|
||||
ack_div.addClass('thumbs up outline icon');
|
||||
}
|
||||
|
||||
if (msg['ack'] == true) {
|
||||
var loader_div = $('#' + loader_id);
|
||||
var ack_div = $('#' + ack_id);
|
||||
loader_div.removeClass('ui active inline loader');
|
||||
loader_div.addClass('ui disabled loader');
|
||||
ack_div.removeClass('thumbs up outline icon');
|
||||
ack_div.addClass('thumbs up outline icon');
|
||||
}
|
||||
|
||||
$('.ui.accordion').accordion('refresh');
|
||||
$('.ui.accordion').accordion('refresh');
|
||||
save_data();
|
||||
}
|
||||
|
||||
function callsign_select(callsign) {
|
||||
@@ -197,16 +344,21 @@ function reset_Tabs() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function openCallsign(evt, callsign) {
|
||||
// This is called when a callsign tab is clicked
|
||||
var i, tabcontent, tablinks;
|
||||
|
||||
tab_content = tab_content_name(callsign);
|
||||
|
||||
tabcontent = document.getElementsByClassName("tabcontent");
|
||||
console.log(tabcontent);
|
||||
for (i = 0; i < tabcontent.length; i++) {
|
||||
tabcontent[i].style.display = "none";
|
||||
}
|
||||
tablinks = document.getElementsByClassName("tablinks");
|
||||
console.log(tablinks);
|
||||
for (i = 0; i < tablinks.length; i++) {
|
||||
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,13 +1,19 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
|
||||
<script src="https://cdn.socket.io/4.1.2/socket.io.min.js" integrity="sha384-toS6mmwu70G0fw54EGlWWeA4z3dyJ+dlXBtSURSKN4vyRFOcxd3Bzjj/AoOwY+Rg" crossorigin="anonymous"></script>
|
||||
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> -->
|
||||
<script src="/static/js/upstream/jquery.min.js"></script>
|
||||
<!-- <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css"> -->
|
||||
<link rel="stylesheet" href="/static/css/upstream/jquery-ui.css">
|
||||
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> -->
|
||||
<script src="/static/js/upstream/jquery-ui.min.js"></script>
|
||||
<!-- <script src="https://cdn.socket.io/4.1.2/socket.io.min.js" integrity="sha384-toS6mmwu70G0fw54EGlWWeA4z3dyJ+dlXBtSURSKN4vyRFOcxd3Bzjj/AoOwY+Rg" crossorigin="anonymous"></script> -->
|
||||
<script src="/static/js/upstream/socket.io.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.js"></script>
|
||||
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.css"> -->
|
||||
<link rel="stylesheet" href="/static/css/upstream/semantic.min.css">
|
||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.js"></script> -->
|
||||
<script src="/static/js/upstream/semantic.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
<link rel="stylesheet" href="/static/css/tabs.css">
|
||||
@@ -17,6 +23,8 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var initial_stats = {{ initial_stats|tojson|safe }};
|
||||
var latitude = parseFloat('{{ latitude|safe }}');
|
||||
var longitude = parseFloat('{{ longitude|safe }}');
|
||||
|
||||
var memory_chart = null
|
||||
var message_chart = null
|
||||
@@ -27,10 +35,18 @@
|
||||
init_chat();
|
||||
reset_Tabs();
|
||||
|
||||
if (location.protocol != 'https:') {
|
||||
console.log("latitude", latitude);
|
||||
console.log("longitude", longitude);
|
||||
|
||||
if (isNaN(latitude) || isNaN(longitude) && location.protocol != 'https:') {
|
||||
// Have to disable the beacon button.
|
||||
$('#send_beacon').prop('disabled', true);
|
||||
}
|
||||
|
||||
$("#wipe_local").click(function() {
|
||||
console.log('Wipe local storage');
|
||||
localStorage.clear();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
@@ -69,6 +85,7 @@
|
||||
</div>
|
||||
<input type="submit" name="submit" class="ui button" id="send_msg" value="Send" />
|
||||
<button type="button" class="ui button" id="send_beacon" value="Send GPS Beacon">Send GPS Beacon</button>
|
||||
<button type="button" class="ui button" id="wipe_local" value="wipe local storage">Wipe LocalStorage</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,7 +95,6 @@
|
||||
<div class="tab" id="callsignTabs"></div>
|
||||
</div>
|
||||
<div class="ten wide column ui raised segment" id="msgsTabsDiv" style="height:450px;padding:0px;">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
|
||||
<script src="https://cdn.socket.io/4.1.2/socket.io.min.js" integrity="sha384-toS6mmwu70G0fw54EGlWWeA4z3dyJ+dlXBtSURSKN4vyRFOcxd3Bzjj/AoOwY+Rg" crossorigin="anonymous"></script>
|
||||
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> -->
|
||||
<script src="/static/js/upstream/jquery.min.js"></script>
|
||||
<!-- <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css"> -->
|
||||
<link rel="stylesheet" href="/static/css/upstream/jquery-ui.css">
|
||||
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> -->
|
||||
<script src="/static/js/upstream/jquery-ui.min.js"></script>
|
||||
<!-- <script src="https://cdn.socket.io/4.1.2/socket.io.min.js" integrity="sha384-toS6mmwu70G0fw54EGlWWeA4z3dyJ+dlXBtSURSKN4vyRFOcxd3Bzjj/AoOwY+Rg" crossorigin="anonymous"></script> -->
|
||||
<script src="/static/js/upstream/socket.io.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.js"></script>
|
||||
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.css"> -->
|
||||
<link rel="stylesheet" href="/static/css/upstream/semantic.min.css">
|
||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.9.0/semantic.min.js"></script> -->
|
||||
<script src="/static/js/upstream/semantic.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
<script src="/static/js/main.js"></script>
|
||||
@@ -16,6 +22,8 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var initial_stats = {{ initial_stats|tojson|safe }};
|
||||
var latitude = parseFloat('{{ latitude|safe }}');
|
||||
var longitude = parseFloat('{{ longitude|safe }}');
|
||||
|
||||
var memory_chart = null
|
||||
var message_chart = null
|
||||
@@ -24,6 +32,14 @@
|
||||
console.log(initial_stats);
|
||||
start_update();
|
||||
init_chat();
|
||||
|
||||
console.log("latitude", latitude);
|
||||
console.log("longitude", longitude);
|
||||
|
||||
if (isNaN(latitude) || isNaN(longitude) && location.protocol != 'https:') {
|
||||
// Have to disable the beacon button.
|
||||
$('#send_beacon').prop('disabled', true);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ RUN set -ex \
|
||||
# Upgrade the package index and install security upgrades
|
||||
&& apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y git build-essential curl vim libffi-dev \
|
||||
&& apt-get install -y git build-essential curl libffi-dev \
|
||||
python3-dev libssl-dev libxml2-dev libxslt-dev telnet sudo \
|
||||
# Install dependencies
|
||||
# Clean up
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN set -ex \
|
||||
# Upgrade the package index and install security upgrades
|
||||
&& apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y git build-essential curl vim libffi-dev \
|
||||
&& apt-get install -y git build-essential curl libffi-dev \
|
||||
python3-dev libssl-dev libxml2-dev libxslt-dev telnet sudo \
|
||||
# Install dependencies
|
||||
# Clean up
|
||||
|
||||
@@ -20,6 +20,14 @@ aprsd.cmds.dev module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
aprsd.cmds.fetch\_stats module
|
||||
------------------------------
|
||||
|
||||
.. automodule:: aprsd.cmds.fetch_stats
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
aprsd.cmds.healthcheck module
|
||||
-----------------------------
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
aprsd.logging package
|
||||
=====================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
aprsd.logging.log module
|
||||
------------------------
|
||||
|
||||
.. automodule:: aprsd.logging.log
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
aprsd.logging.rich module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: aprsd.logging.rich
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: aprsd.logging
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
+12
-12
@@ -10,7 +10,7 @@ Subpackages
|
||||
aprsd.clients
|
||||
aprsd.cmds
|
||||
aprsd.conf
|
||||
aprsd.logging
|
||||
aprsd.log
|
||||
aprsd.packets
|
||||
aprsd.plugins
|
||||
aprsd.rpc
|
||||
@@ -21,14 +21,6 @@ Subpackages
|
||||
Submodules
|
||||
----------
|
||||
|
||||
aprsd.aprsd module
|
||||
------------------
|
||||
|
||||
.. automodule:: aprsd.aprsd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
aprsd.cli\_helper module
|
||||
------------------------
|
||||
|
||||
@@ -53,10 +45,10 @@ aprsd.exception module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
aprsd.flask module
|
||||
------------------
|
||||
aprsd.main module
|
||||
-----------------
|
||||
|
||||
.. automodule:: aprsd.flask
|
||||
.. automodule:: aprsd.main
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -93,6 +85,14 @@ aprsd.stats module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
aprsd.wsgi module
|
||||
-----------------
|
||||
|
||||
.. automodule:: aprsd.wsgi
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user