mirror of
https://github.com/craigerl/aprsd.git
synced 2025-06-26 05:55:26 -04:00
New Admin ui send message page working.
This commit is contained in:
parent
6d3258e833
commit
23cbf32814
@ -130,6 +130,10 @@ class Aprsdis(aprslib.IS):
|
|||||||
|
|
||||||
# sock.recv returns empty if the connection drops
|
# sock.recv returns empty if the connection drops
|
||||||
if not short_buf:
|
if not short_buf:
|
||||||
|
if not blocking:
|
||||||
|
# We could just not be blocking, so empty is expected
|
||||||
|
continue
|
||||||
|
else:
|
||||||
self.logger.error("socket.recv(): returned empty")
|
self.logger.error("socket.recv(): returned empty")
|
||||||
raise aprslib.ConnectionDrop("connection dropped")
|
raise aprslib.ConnectionDrop("connection dropped")
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
314
aprsd/flask.py
314
aprsd/flask.py
@ -4,8 +4,10 @@ import logging
|
|||||||
from logging import NullHandler
|
from logging import NullHandler
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import aprslib
|
||||||
from aprslib.exceptions import LoginError
|
from aprslib.exceptions import LoginError
|
||||||
import flask
|
import flask
|
||||||
from flask import request
|
from flask import request
|
||||||
@ -14,7 +16,7 @@ from flask_httpauth import HTTPBasicAuth
|
|||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
|
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import client, kissclient, messaging, packets, plugin, stats, utils
|
from aprsd import client, kissclient, messaging, packets, plugin, stats, threads, utils
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
@ -22,6 +24,72 @@ LOG = logging.getLogger("APRSD")
|
|||||||
auth = HTTPBasicAuth()
|
auth = HTTPBasicAuth()
|
||||||
users = None
|
users = None
|
||||||
|
|
||||||
|
class SentMessages:
|
||||||
|
_instance = None
|
||||||
|
lock = None
|
||||||
|
|
||||||
|
msgs = {}
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""This magic turns this into a singleton."""
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super().__new__(cls)
|
||||||
|
# Put any initialization here.
|
||||||
|
cls.lock = threading.Lock()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def add(self, msg):
|
||||||
|
with self.lock:
|
||||||
|
self.msgs[msg.id] = self._create(msg.id)
|
||||||
|
self.msgs[msg.id]["from"] = msg.fromcall
|
||||||
|
self.msgs[msg.id]["to"] = msg.tocall
|
||||||
|
self.msgs[msg.id]["message"] = msg.message.rstrip("\n")
|
||||||
|
self.msgs[msg.id]["raw"] = str(msg).rstrip("\n")
|
||||||
|
|
||||||
|
|
||||||
|
def _create(self, id):
|
||||||
|
return {
|
||||||
|
"id": id,
|
||||||
|
"ts": time.time(),
|
||||||
|
"ack": False,
|
||||||
|
"from": None,
|
||||||
|
"to": None,
|
||||||
|
"raw": None,
|
||||||
|
"message": None,
|
||||||
|
"status": None,
|
||||||
|
"last_update": None,
|
||||||
|
"reply": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
with self.lock:
|
||||||
|
return len(self.msgs.keys())
|
||||||
|
|
||||||
|
def get(self, id):
|
||||||
|
with self.lock:
|
||||||
|
if id in self.msgs:
|
||||||
|
return self.msgs[id]
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
with self.lock:
|
||||||
|
return self.msgs
|
||||||
|
|
||||||
|
def set_status(self, id, status):
|
||||||
|
with self.lock:
|
||||||
|
self.msgs[id]["last_update"] = str(datetime.datetime.now())
|
||||||
|
self.msgs[id]["status"] = status
|
||||||
|
|
||||||
|
def ack(self, id):
|
||||||
|
"""The message got an ack!"""
|
||||||
|
with self.lock:
|
||||||
|
self.msgs[id]["last_update"] = str(datetime.datetime.now())
|
||||||
|
self.msgs[id]["ack"] = True
|
||||||
|
|
||||||
|
def reply(self, id, packet):
|
||||||
|
"""We got a packet back from the sent message."""
|
||||||
|
with self.lock:
|
||||||
|
self.msgs[id]["reply"] = packet
|
||||||
|
|
||||||
|
|
||||||
# HTTPBasicAuth doesn't work on a class method.
|
# HTTPBasicAuth doesn't work on a class method.
|
||||||
# This has to be out here. Rely on the APRSDFlask
|
# This has to be out here. Rely on the APRSDFlask
|
||||||
@ -34,6 +102,161 @@ def verify_password(username, password):
|
|||||||
return username
|
return username
|
||||||
|
|
||||||
|
|
||||||
|
class SendMessageThread(threads.APRSDThread):
|
||||||
|
"""Thread for sending a message from web."""
|
||||||
|
|
||||||
|
aprsis_client = None
|
||||||
|
request = None
|
||||||
|
got_ack = False
|
||||||
|
|
||||||
|
def __init__(self, config, info, msg):
|
||||||
|
self.config = config
|
||||||
|
self.request = info
|
||||||
|
self.msg = msg
|
||||||
|
msg = "({} -> {}) : {}".format(
|
||||||
|
info["from"],
|
||||||
|
info["to"],
|
||||||
|
info["message"],
|
||||||
|
)
|
||||||
|
super().__init__(f"WEB_SEND_MSG-{msg}")
|
||||||
|
|
||||||
|
def setup_connection(self):
|
||||||
|
user = self.request["from"]
|
||||||
|
password = self.request["password"]
|
||||||
|
host = self.config["aprs"].get("host", "rotate.aprs.net")
|
||||||
|
port = self.config["aprs"].get("port", 14580)
|
||||||
|
connected = False
|
||||||
|
backoff = 1
|
||||||
|
while not connected:
|
||||||
|
try:
|
||||||
|
LOG.info("Creating aprslib client")
|
||||||
|
aprs_client = client.Aprsdis(
|
||||||
|
user,
|
||||||
|
passwd=password,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
)
|
||||||
|
# Force the logging to be the same
|
||||||
|
aprs_client.logger = LOG
|
||||||
|
aprs_client.connect()
|
||||||
|
connected = True
|
||||||
|
backoff = 1
|
||||||
|
except LoginError as e:
|
||||||
|
LOG.error(f"Failed to login to APRS-IS Server '{e}'")
|
||||||
|
connected = False
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
||||||
|
time.sleep(backoff)
|
||||||
|
backoff = backoff * 2
|
||||||
|
continue
|
||||||
|
LOG.debug(f"Logging in to APRS-IS with user '{user}'")
|
||||||
|
return aprs_client
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
LOG.debug("Starting")
|
||||||
|
from_call = self.request["from"]
|
||||||
|
self.request["password"]
|
||||||
|
to_call = self.request["to"]
|
||||||
|
message = self.request["message"]
|
||||||
|
LOG.info(
|
||||||
|
"From: '{}' To: '{}' Send '{}'".format(
|
||||||
|
from_call,
|
||||||
|
to_call,
|
||||||
|
message,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.aprs_client = self.setup_connection()
|
||||||
|
except LoginError as e:
|
||||||
|
f"Failed to setup Connection {e}"
|
||||||
|
|
||||||
|
self.msg.send_direct(aprsis_client=self.aprs_client)
|
||||||
|
SentMessages().set_status(self.msg.id, "Sent")
|
||||||
|
|
||||||
|
while not self.thread_stop:
|
||||||
|
can_loop = self.loop()
|
||||||
|
if not can_loop:
|
||||||
|
self.stop()
|
||||||
|
threads.APRSDThreadList().remove(self)
|
||||||
|
LOG.debug("Exiting")
|
||||||
|
|
||||||
|
def rx_packet(self, packet):
|
||||||
|
global got_ack, got_response
|
||||||
|
# LOG.debug("Got packet back {}".format(packet))
|
||||||
|
resp = packet.get("response", None)
|
||||||
|
if resp == "ack":
|
||||||
|
ack_num = packet.get("msgNo")
|
||||||
|
LOG.info(f"We got ack for our sent message {ack_num}")
|
||||||
|
messaging.log_packet(packet)
|
||||||
|
SentMessages().ack(self.msg.id)
|
||||||
|
stats.APRSDStats().ack_rx_inc()
|
||||||
|
self.got_ack = True
|
||||||
|
if self.request["wait_reply"] == "0":
|
||||||
|
# We aren't waiting for a reply, so we can bail
|
||||||
|
self.thread_stop = self.aprs_client.thread_stop = True
|
||||||
|
else:
|
||||||
|
packets.PacketList().add(packet)
|
||||||
|
stats.APRSDStats().msgs_rx_inc()
|
||||||
|
message = packet.get("message_text", None)
|
||||||
|
fromcall = packet["from"]
|
||||||
|
msg_number = packet.get("msgNo", "0")
|
||||||
|
messaging.log_message(
|
||||||
|
"Received Message",
|
||||||
|
packet["raw"],
|
||||||
|
message,
|
||||||
|
fromcall=fromcall,
|
||||||
|
ack=msg_number,
|
||||||
|
)
|
||||||
|
got_response = True
|
||||||
|
SentMessages().reply(self.msg.id, packet)
|
||||||
|
SentMessages().set_status(self.msg.id, "Got Reply")
|
||||||
|
|
||||||
|
# Send the ack back?
|
||||||
|
ack = messaging.AckMessage(
|
||||||
|
self.request["from"],
|
||||||
|
fromcall,
|
||||||
|
msg_id=msg_number,
|
||||||
|
)
|
||||||
|
ack.send_direct()
|
||||||
|
SentMessages().set_status(self.msg.id, "Ack Sent")
|
||||||
|
|
||||||
|
# Now we can exit, since we are done.
|
||||||
|
if self.got_ack:
|
||||||
|
self.thread_stop = self.aprs_client.thread_stop = True
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
LOG.debug("LOOP Start")
|
||||||
|
try:
|
||||||
|
# This will register a packet consumer with aprslib
|
||||||
|
# When new packets come in the consumer will process
|
||||||
|
# the packet
|
||||||
|
self.aprs_client.consumer(self.rx_packet, raw=False, blocking=False)
|
||||||
|
except aprslib.exceptions.ConnectionDrop:
|
||||||
|
LOG.error("Connection dropped, reconnecting")
|
||||||
|
time.sleep(5)
|
||||||
|
# Force the deletion of the client object connected to aprs
|
||||||
|
# This will cause a reconnect, next time client.get_client()
|
||||||
|
# is called
|
||||||
|
del self.aprs_client
|
||||||
|
connecting = True
|
||||||
|
counter = 0;
|
||||||
|
while connecting:
|
||||||
|
try:
|
||||||
|
self.aprs_client = self.setup_connection()
|
||||||
|
connecting = False
|
||||||
|
except Exception:
|
||||||
|
LOG.error("Couldn't connect")
|
||||||
|
counter += 1
|
||||||
|
if counter >= 3:
|
||||||
|
LOG.error("Reached reconnect limit.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
LOG.debug("LOOP END")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class APRSDFlask(flask_classful.FlaskView):
|
class APRSDFlask(flask_classful.FlaskView):
|
||||||
config = None
|
config = None
|
||||||
|
|
||||||
@ -118,68 +341,52 @@ class APRSDFlask(flask_classful.FlaskView):
|
|||||||
|
|
||||||
return flask.render_template("messages.html", messages=json.dumps(msgs))
|
return flask.render_template("messages.html", messages=json.dumps(msgs))
|
||||||
|
|
||||||
def setup_connection(self):
|
@auth.login_required
|
||||||
user = self.config["aprs"]["login"]
|
def send_message_status(self):
|
||||||
password = self.config["aprs"]["password"]
|
LOG.debug(request)
|
||||||
host = self.config["aprs"].get("host", "rotate.aprs.net")
|
msgs = SentMessages()
|
||||||
port = self.config["aprs"].get("port", 14580)
|
info = msgs.get_all()
|
||||||
connected = False
|
return json.dumps(info)
|
||||||
backoff = 1
|
|
||||||
while not connected:
|
|
||||||
try:
|
|
||||||
LOG.info("Creating aprslib client")
|
|
||||||
aprs_client = client.Aprsdis(
|
|
||||||
user,
|
|
||||||
passwd=password,
|
|
||||||
host=host,
|
|
||||||
port=port,
|
|
||||||
)
|
|
||||||
# Force the logging to be the same
|
|
||||||
aprs_client.logger = LOG
|
|
||||||
aprs_client.connect()
|
|
||||||
connected = True
|
|
||||||
backoff = 1
|
|
||||||
except LoginError as e:
|
|
||||||
LOG.error("Failed to login to APRS-IS Server '{}'".format(e))
|
|
||||||
connected = False
|
|
||||||
raise e
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error("Unable to connect to APRS-IS server. '{}' ".format(e))
|
|
||||||
time.sleep(backoff)
|
|
||||||
backoff = backoff * 2
|
|
||||||
continue
|
|
||||||
LOG.debug("Logging in to APRS-IS with user '%s'" % user)
|
|
||||||
return aprs_client
|
|
||||||
|
|
||||||
|
@auth.login_required
|
||||||
def send_message(self):
|
def send_message(self):
|
||||||
|
LOG.debug(request)
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
from_call = request.form["from_call"]
|
info = {
|
||||||
to_call = request.form["to_call"]
|
"from": request.form["from_call"],
|
||||||
message = request.form["message"]
|
"to": request.form["to_call"],
|
||||||
LOG.info(
|
"password": request.form["from_call_password"],
|
||||||
"From: '{}' To: '{}' Send '{}'".format(
|
"message": request.form["message"],
|
||||||
from_call,
|
"wait_reply": request.form["wait_reply"],
|
||||||
to_call,
|
}
|
||||||
message,
|
LOG.debug(info)
|
||||||
),
|
msg = messaging.TextMessage(
|
||||||
|
info["from"], info["to"],
|
||||||
|
info["message"],
|
||||||
)
|
)
|
||||||
|
msgs = SentMessages()
|
||||||
|
msgs.add(msg)
|
||||||
|
msgs.set_status(msg.id, "Sending")
|
||||||
|
|
||||||
try:
|
send_message_t = SendMessageThread(self.config, info, msg)
|
||||||
aprsis_client = self.setup_connection()
|
send_message_t.start()
|
||||||
except LoginError as e:
|
|
||||||
result = "Failed to setup Connection {}".format(e)
|
|
||||||
|
|
||||||
msg = messaging.TextMessage(from_call, to_call, message)
|
|
||||||
msg.send_direct(aprsis_client=aprsis_client)
|
info["from"]
|
||||||
result = "Message sent"
|
result = "sending"
|
||||||
|
msg_id = msg.id
|
||||||
|
result = {
|
||||||
|
"msg_id": msg_id,
|
||||||
|
"status": "sending",
|
||||||
|
}
|
||||||
|
return json.dumps(result)
|
||||||
else:
|
else:
|
||||||
from_call = self.config["aprs"]["login"]
|
result = "fail"
|
||||||
result = ""
|
|
||||||
|
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
"send-message.html",
|
"send-message.html",
|
||||||
from_call=from_call,
|
callsign=self.config["aprs"]["login"],
|
||||||
result=result,
|
version=aprsd.__version__,
|
||||||
)
|
)
|
||||||
|
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
@ -292,6 +499,7 @@ def init_flask(config, loglevel, quiet):
|
|||||||
flask_app.route("/messages", methods=["GET"])(server.messages)
|
flask_app.route("/messages", methods=["GET"])(server.messages)
|
||||||
flask_app.route("/packets", methods=["GET"])(server.packets)
|
flask_app.route("/packets", methods=["GET"])(server.packets)
|
||||||
flask_app.route("/send-message", methods=["GET", "POST"])(server.send_message)
|
flask_app.route("/send-message", methods=["GET", "POST"])(server.send_message)
|
||||||
|
flask_app.route("/send-message-status", methods=["GET"])(server.send_message_status)
|
||||||
flask_app.route("/save", methods=["GET"])(server.save)
|
flask_app.route("/save", methods=["GET"])(server.save)
|
||||||
flask_app.route("/plugins", methods=["GET"])(server.plugins)
|
flask_app.route("/plugins", methods=["GET"])(server.plugins)
|
||||||
return flask_app
|
return flask_app
|
||||||
|
@ -391,6 +391,7 @@ class TextMessage(Message):
|
|||||||
)
|
)
|
||||||
cl.send(self)
|
cl.send(self)
|
||||||
stats.APRSDStats().msgs_tx_inc()
|
stats.APRSDStats().msgs_tx_inc()
|
||||||
|
packets.PacketList().add(self.dict())
|
||||||
|
|
||||||
|
|
||||||
class SendMessageThread(threads.APRSDThread):
|
class SendMessageThread(threads.APRSDThread):
|
||||||
|
@ -69,6 +69,7 @@ class WatchList:
|
|||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
callsigns = {}
|
callsigns = {}
|
||||||
|
config = None
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
@ -97,7 +98,7 @@ class WatchList:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
if "watch_list" in self.config["aprsd"]:
|
if self.config and "watch_list" in self.config["aprsd"]:
|
||||||
return self.config["aprsd"]["watch_list"].get("enabled", False)
|
return self.config["aprsd"]["watch_list"].get("enabled", False)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
74
aprsd/web/static/js/send-message.js
Normal file
74
aprsd/web/static/js/send-message.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
msgs_list = {};
|
||||||
|
|
||||||
|
function size_dict(d){c=0; for (i in d) ++c; return c}
|
||||||
|
|
||||||
|
function update_messages(data) {
|
||||||
|
msgs_cnt = size_dict(data);
|
||||||
|
$('#msgs_count').html(msgs_cnt);
|
||||||
|
|
||||||
|
var msgsdiv = $("#msgsDiv");
|
||||||
|
//nuke the contents first, then add to it.
|
||||||
|
if (size_dict(msgs_list) == 0 && size_dict(data) > 0) {
|
||||||
|
msgsdiv.html('')
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.each(data, function(i, val) {
|
||||||
|
if ( msgs_list.hasOwnProperty(val["ts"]) == false ) {
|
||||||
|
// Store the packet
|
||||||
|
msgs_list[val["ts"]] = val;
|
||||||
|
ts_str = val["ts"].toString();
|
||||||
|
ts = ts_str.split(".")[0]*1000;
|
||||||
|
var d = new Date(ts).toLocaleDateString("en-US")
|
||||||
|
var t = new Date(ts).toLocaleTimeString("en-US")
|
||||||
|
|
||||||
|
from = val['from']
|
||||||
|
title_id = 'title_tx'
|
||||||
|
var from_to = d + " " + t + " " + from + " > "
|
||||||
|
|
||||||
|
if (val.hasOwnProperty('to')) {
|
||||||
|
from_to = from_to + val['to']
|
||||||
|
}
|
||||||
|
from_to = from_to + " - " + val['raw']
|
||||||
|
|
||||||
|
id = ts_str.split('.')[0]
|
||||||
|
pretty_id = "pretty_" + id
|
||||||
|
loader_id = "loader_" + id
|
||||||
|
reply_id = "reply_" + id
|
||||||
|
json_pretty = Prism.highlight(JSON.stringify(val, null, '\t'), Prism.languages.json, 'json');
|
||||||
|
msg_html = '<div class="ui title" id="' + title_id + '"><i class="dropdown icon"></i>';
|
||||||
|
msg_html += '<div class="ui active inline loader" id="' + loader_id +'" data-content="Waiting for Ack"></div> ';
|
||||||
|
msg_html += '<i class="thumbs down outline icon" id="' + reply_id + '" data-content="Waiting for Reply"></i> ' + from_to + '</div>';
|
||||||
|
msg_html += '<div class="content"><p class="transition hidden"><pre id="' + pretty_id + '" class="language-json">' + json_pretty + '</p></p></div>'
|
||||||
|
msgsdiv.prepend(msg_html);
|
||||||
|
} else {
|
||||||
|
// We have an existing entry
|
||||||
|
msgs_list[val["ts"]] = val;
|
||||||
|
ts_str = val["ts"].toString();
|
||||||
|
id = ts_str.split('.')[0]
|
||||||
|
pretty_id = "pretty_" + id
|
||||||
|
loader_id = "loader_" + id
|
||||||
|
reply_id = "reply_" + id
|
||||||
|
var pretty_pre = $("#" + pretty_id);
|
||||||
|
if (val['ack'] == true) {
|
||||||
|
var loader_div = $('#' + loader_id);
|
||||||
|
loader_div.removeClass('ui active inline loader');
|
||||||
|
loader_div.addClass('ui disabled loader');
|
||||||
|
loader_div.attr('data-content', 'Got reply');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val['reply'] !== null) {
|
||||||
|
var reply_div = $('#' + reply_id);
|
||||||
|
reply_div.removeClass("thumbs down outline icon");
|
||||||
|
reply_div.addClass('thumbs up outline icon');
|
||||||
|
reply_div.attr('data-content', 'Got Reply');
|
||||||
|
}
|
||||||
|
|
||||||
|
pretty_pre.html('');
|
||||||
|
json_pretty = Prism.highlight(JSON.stringify(val, null, '\t'), Prism.languages.json, 'json');
|
||||||
|
pretty_pre.html(json_pretty);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.ui.accordion').accordion('refresh');
|
||||||
|
|
||||||
|
}
|
@ -4,22 +4,112 @@
|
|||||||
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
<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://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/prism.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components/prism-json.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/themes/prism-tomorrow.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/static/css/index.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/tabs.css">
|
||||||
|
<script src="/static/js/send-message.js"></script>
|
||||||
|
|
||||||
|
<script language="JavaScript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
$("#sendform").submit(function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var $checkboxes = $(this).find('input[type=checkbox]');
|
||||||
|
|
||||||
|
//loop through the checkboxes and change to hidden fields
|
||||||
|
$checkboxes.each(function() {
|
||||||
|
if ($(this)[0].checked) {
|
||||||
|
$(this).attr('type', 'hidden');
|
||||||
|
$(this).val(1);
|
||||||
|
} else {
|
||||||
|
$(this).attr('type', 'hidden');
|
||||||
|
$(this).val(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var posting = $.post("/send-message", $("#sendform").serialize());
|
||||||
|
|
||||||
|
posting.done(function(data, status){
|
||||||
|
console.log("Data: " + data + "\nStatus: " + status);
|
||||||
|
});
|
||||||
|
|
||||||
|
//loop through the checkboxes and change to hidden fields
|
||||||
|
$checkboxes.each(function() {
|
||||||
|
$(this).attr('type', 'checkbox');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
(function sent_msg_worker() {
|
||||||
|
$.ajax({
|
||||||
|
url: "/send-message-status",
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(data) {
|
||||||
|
update_messages(data);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
setTimeout(sent_msg_worker, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<form action="send-message" method="POST" name="send-message">
|
<div class='ui text container'>
|
||||||
|
<h1 class='ui dividing header'>APRSD {{ version }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='ui grid text container'>
|
||||||
|
<div class='left floated ten wide column'>
|
||||||
|
<span style='color: green'>{{ callsign }}</span>
|
||||||
|
connected to
|
||||||
|
<span style='color: blue' id='aprsis'>NONE</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='right floated four wide column'>
|
||||||
|
<span id='uptime'>NONE</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="ui dividing header">Send Message Form</h3>
|
||||||
|
<form id="sendform" name="sendmsg" action="">
|
||||||
<p><label for="from_call">From Callsign:</label>
|
<p><label for="from_call">From Callsign:</label>
|
||||||
<input type="text" name="from_call" id="fcall" value="{{ from_call }}"></p>
|
<input type="text" name="from_call" id="fcall" value="WB4BOR"></p>
|
||||||
|
<p><label for="from_call_password">Password:</label>
|
||||||
|
<input type="text" name="from_call_password" value="24496"></p>
|
||||||
|
|
||||||
<p><label for="to_call">To Callsign:</label>
|
<p><label for="to_call">To Callsign:</label>
|
||||||
<input type="text" name="to_call" id="tcall"></p>
|
<input type="text" name="to_call" id="tcall" value="WB4BOR-11"></p>
|
||||||
|
|
||||||
<p><label for="message">Message:</label>
|
<p><label for="message">Message:</label>
|
||||||
<input type="text" name="message" id="message"></p>
|
<input type="text" name="message" id="message" value="ping"></p>
|
||||||
|
|
||||||
<input value="Submit" type="submit">
|
<p><label for="wait">Wait for Reply?</label>
|
||||||
|
<input type="checkbox" name="wait_reply" id="wait_reply" value="off" checked>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<input type="submit" name="submit" class="button" id="send_msg" value="Send" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h3 class="ui dividing header">Messages (<span id="msgs_count">0</span>)</h3>
|
||||||
|
<div class="ui styled fluid accordion" id="accordion">
|
||||||
|
<div id="msgsDiv" class="ui mini text">Messages</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user