forked from platypush/platypush
vue.js migration commit - WIP
This commit is contained in:
parent
1ad86428c8
commit
8b478ede45
12 changed files with 13821 additions and 437 deletions
|
@ -3,23 +3,22 @@
|
|||
.. license: MIT
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from threading import Thread
|
||||
|
||||
from platypush.bus import Bus
|
||||
from platypush.config import Config
|
||||
from platypush.context import get_backend, get_plugin
|
||||
from platypush.utils import get_message_class_by_type, set_timeout, clear_timeout
|
||||
from platypush.context import get_backend
|
||||
from platypush.utils import set_timeout, clear_timeout, \
|
||||
get_redis_queue_name_by_message, set_thread_name
|
||||
|
||||
from platypush.event import EventGenerator
|
||||
from platypush.message import Message
|
||||
from platypush.message.event import Event, StopEvent
|
||||
from platypush.message.request import Request
|
||||
from platypush.message.response import Response
|
||||
from platypush.utils import get_redis_queue_name_by_message, set_thread_name
|
||||
|
||||
|
||||
class Backend(Thread, EventGenerator):
|
||||
|
@ -64,11 +63,10 @@ class Backend(Thread, EventGenerator):
|
|||
else None
|
||||
|
||||
if 'logging' in kwargs:
|
||||
self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
|
||||
self.logger.setLevel(getattr(logging, kwargs.get('logging').upper()))
|
||||
|
||||
Thread.__init__(self)
|
||||
|
||||
|
||||
def on_message(self, msg):
|
||||
"""
|
||||
Callback when a message is received on the backend.
|
||||
|
@ -76,7 +74,8 @@ class Backend(Thread, EventGenerator):
|
|||
It should be called by the derived classes whenever
|
||||
a new message should be processed.
|
||||
|
||||
:param msg: Received message. It can be either a key-value dictionary, a platypush.message.Message object, or a string/byte UTF-8 encoded string
|
||||
:param msg: Received message. It can be either a key-value dictionary, a platypush.message.Message object,
|
||||
or a string/byte UTF-8 encoded string
|
||||
"""
|
||||
|
||||
msg = Message.build(msg)
|
||||
|
|
|
@ -25,7 +25,9 @@ class HttpBackend(Backend):
|
|||
}' \\
|
||||
http://localhost:8008/execute
|
||||
|
||||
* To interact with your system (and control plugins and backends) through the Platypush web panel, by default available on your web root document. Any plugin that you have configured and available as a panel plugin will appear on the web panel as well as a tab.
|
||||
* To interact with your system (and control plugins and backends) through the Platypush web panel,
|
||||
by default available on your web root document. Any plugin that you have configured and available as a panel
|
||||
plugin will appear on the web panel as well as a tab.
|
||||
|
||||
* To display a fullscreen dashboard with your configured widgets, by default available under ``/dashboard``
|
||||
|
||||
|
@ -69,9 +71,9 @@ class HttpBackend(Backend):
|
|||
|
||||
def __init__(self, port=_DEFAULT_HTTP_PORT,
|
||||
websocket_port=_DEFAULT_WEBSOCKET_PORT,
|
||||
disable_websocket=False, dashboard={}, resource_dirs={},
|
||||
disable_websocket=False, dashboard=None, resource_dirs=None,
|
||||
ssl_cert=None, ssl_key=None, ssl_cafile=None, ssl_capath=None,
|
||||
maps={}, run_externally=False, uwsgi_args=None, **kwargs):
|
||||
maps=None, run_externally=False, uwsgi_args=None, **kwargs):
|
||||
"""
|
||||
:param port: Listen port for the web server (default: 8008)
|
||||
:type port: int
|
||||
|
@ -88,10 +90,12 @@ class HttpBackend(Backend):
|
|||
:param ssl_key: Set it to the path of your key file if you want to enable HTTPS (default: None)
|
||||
:type ssl_key: str
|
||||
|
||||
:param ssl_cafile: Set it to the path of your certificate authority file if you want to enable HTTPS (default: None)
|
||||
:param ssl_cafile: Set it to the path of your certificate authority file if you want to enable HTTPS
|
||||
(default: None)
|
||||
:type ssl_cafile: str
|
||||
|
||||
:param ssl_capath: Set it to the path of your certificate authority directory if you want to enable HTTPS (default: None)
|
||||
:param ssl_capath: Set it to the path of your certificate authority directory if you want to enable HTTPS
|
||||
(default: None)
|
||||
:type ssl_capath: str
|
||||
|
||||
:param resource_dirs: Static resources directories that will be
|
||||
|
@ -100,7 +104,8 @@ class HttpBackend(Backend):
|
|||
the value is the absolute path to expose.
|
||||
:type resource_dirs: dict[str, str]
|
||||
|
||||
:param dashboard: Set it if you want to use the dashboard service. It will contain the configuration for the widgets to be used (look under ``platypush/backend/http/templates/widgets/`` for the available widgets).
|
||||
:param dashboard: Set it if you want to use the dashboard service. It will contain the configuration for the
|
||||
widgets to be used (look under ``platypush/backend/http/templates/widgets/`` for the available widgets).
|
||||
|
||||
Example configuration::
|
||||
|
||||
|
@ -119,14 +124,15 @@ class HttpBackend(Backend):
|
|||
-
|
||||
widget: image-carousel # Image carousel
|
||||
columns: 6
|
||||
images_path: ~/Dropbox/Photos/carousel # Absolute path (valid as long as it's a subdirectory of one of the available `resource_dirs`)
|
||||
# Absolute path (valid as long as it's a subdirectory of one of the available `resource_dirs`)
|
||||
images_path: ~/Dropbox/Photos/carousel
|
||||
refresh_seconds: 15
|
||||
-
|
||||
widget: rss-news # RSS feeds widget
|
||||
# Requires backend.http.poll to be enabled with some RSS sources and write them to sqlite db
|
||||
columns: 6
|
||||
limit: 25
|
||||
db: "sqlite:////home/blacklight/.local/share/platypush/feeds/rss.db"
|
||||
db: "sqlite:////home/user/.local/share/platypush/feeds/rss.db"
|
||||
|
||||
:type dashboard: dict
|
||||
|
||||
|
@ -155,13 +161,18 @@ class HttpBackend(Backend):
|
|||
|
||||
self.port = port
|
||||
self.websocket_port = websocket_port
|
||||
self.dashboard = dashboard
|
||||
self.maps = maps
|
||||
self.dashboard = dashboard or {}
|
||||
self.maps = maps or {}
|
||||
self.server_proc = None
|
||||
self.disable_websocket = disable_websocket
|
||||
self.websocket_thread = None
|
||||
self.resource_dirs = { name: os.path.abspath(
|
||||
os.path.expanduser(d)) for name, d in resource_dirs.items() }
|
||||
|
||||
if resource_dirs:
|
||||
self.resource_dirs = {name: os.path.abspath(
|
||||
os.path.expanduser(d)) for name, d in resource_dirs.items()}
|
||||
else:
|
||||
self.resource_dirs = {}
|
||||
|
||||
self.active_websockets = set()
|
||||
self.run_externally = run_externally
|
||||
self.uwsgi_args = uwsgi_args or []
|
||||
|
@ -175,11 +186,9 @@ class HttpBackend(Backend):
|
|||
self.uwsgi_args = [str(_) for _ in self.uwsgi_args] + \
|
||||
['--module', 'platypush.backend.http.uwsgi', '--enable-threads']
|
||||
|
||||
|
||||
def send_message(self, msg):
|
||||
def send_message(self, msg, **kwargs):
|
||||
self.logger.warning('Use cURL or any HTTP client to query the HTTP backend')
|
||||
|
||||
|
||||
def on_stop(self):
|
||||
""" On backend stop """
|
||||
self.logger.info('Received STOP event on HttpBackend')
|
||||
|
@ -196,23 +205,22 @@ class HttpBackend(Backend):
|
|||
""" Notify all the connected web clients (over websocket) of a new event """
|
||||
import websockets
|
||||
|
||||
async def send_event(websocket):
|
||||
async def send_event(ws):
|
||||
try:
|
||||
await websocket.send(str(event))
|
||||
await ws.send(str(event))
|
||||
except Exception as e:
|
||||
self.logger.warning('Error on websocket send_event: {}'.format(e))
|
||||
|
||||
loop = get_or_create_event_loop()
|
||||
|
||||
websockets = self.active_websockets.copy()
|
||||
for websocket in websockets:
|
||||
wss = self.active_websockets.copy()
|
||||
for websocket in wss:
|
||||
try:
|
||||
loop.run_until_complete(send_event(websocket))
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
self.logger.info('Client connection lost')
|
||||
self.active_websockets.remove(websocket)
|
||||
|
||||
|
||||
def websocket(self):
|
||||
""" Websocket main server """
|
||||
import websockets
|
||||
|
@ -222,7 +230,7 @@ class HttpBackend(Backend):
|
|||
address = websocket.remote_address[0] if websocket.remote_address \
|
||||
else '<unknown client>'
|
||||
|
||||
self.logger.info('New websocket connection from {}'.format(address))
|
||||
self.logger.info('New websocket connection from {} on path {}'.format(address, path))
|
||||
self.active_websockets.add(websocket)
|
||||
|
||||
try:
|
||||
|
@ -258,7 +266,6 @@ class HttpBackend(Backend):
|
|||
|
||||
return proc
|
||||
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
|
||||
|
@ -274,8 +281,11 @@ class HttpBackend(Backend):
|
|||
self.server_proc.join()
|
||||
elif self.uwsgi_args:
|
||||
uwsgi_cmd = ['uwsgi'] + self.uwsgi_args
|
||||
self.logger.info
|
||||
self.logger.info('Starting uWSGI with arguments {}'.format(uwsgi_cmd))
|
||||
self.server_proc = subprocess.Popen(uwsgi_cmd)
|
||||
else:
|
||||
raise EnvironmentError('The web server is configured to be launched externally but ' +
|
||||
'no uwsgi_args were provide')
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -17,10 +17,12 @@ __routes__ = [
|
|||
index,
|
||||
]
|
||||
|
||||
|
||||
@index.route('/')
|
||||
def index():
|
||||
""" Route to the main web panel """
|
||||
if not authentication_ok(request): return authenticate()
|
||||
if not authentication_ok(request):
|
||||
return authenticate()
|
||||
|
||||
# These plugins have their own template file but won't be shown as a tab in
|
||||
# the web panel. This is usually the case for plugins that only include JS
|
||||
|
@ -34,7 +36,7 @@ def index():
|
|||
hidden_plugins = {}
|
||||
|
||||
for plugin, conf in configured_plugins.items():
|
||||
template_file = os.path.join('plugins', plugin + '.html')
|
||||
template_file = os.path.join('panel', plugin, 'index.html')
|
||||
if os.path.isfile(os.path.join(template_folder, template_file)):
|
||||
if plugin in _hidden_plugins:
|
||||
hidden_plugins[plugin] = conf
|
||||
|
@ -43,10 +45,10 @@ def index():
|
|||
|
||||
http_conf = Config.get('backend.http')
|
||||
return render_template('index.html', plugins=enabled_plugins,
|
||||
hidden_plugins=hidden_plugins, utils=HttpUtils,
|
||||
token=Config.get('token'),
|
||||
websocket_port=get_websocket_port(),
|
||||
has_ssl=http_conf.get('ssl_cert') is not None)
|
||||
hidden_plugins=hidden_plugins, utils=HttpUtils,
|
||||
token=Config.get('token'),
|
||||
websocket_port=get_websocket_port(),
|
||||
has_ssl=http_conf.get('ssl_cert') is not None)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
1311
platypush/backend/http/static/css/jquery-ui.css
vendored
Normal file
1311
platypush/backend/http/static/css/jquery-ui.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,372 +1,32 @@
|
|||
$(document).ready(function() {
|
||||
var websocket,
|
||||
pendingConnection = false,
|
||||
openedWebsocket,
|
||||
dateTimeInterval,
|
||||
websocketTimeoutId,
|
||||
modalFadingInTimeoutId,
|
||||
websocketReconnectMsecs = 30000,
|
||||
eventListeners = [];
|
||||
// Declaration of the main vue app
|
||||
var app;
|
||||
|
||||
var initWebsocket = function() {
|
||||
try {
|
||||
url_prefix = window.has_ssl ? 'wss://' : 'ws://';
|
||||
websocket = new WebSocket(url_prefix + window.location.hostname + ':' + window.websocket_port);
|
||||
} catch (err) {
|
||||
websocket = undefined;
|
||||
return;
|
||||
}
|
||||
function ready(callback){
|
||||
if (document.readyState!='loading') callback();
|
||||
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
|
||||
else document.attachEvent('onreadystatechange', function(){
|
||||
if (document.readyState=='complete') callback();
|
||||
});
|
||||
}
|
||||
|
||||
pendingConnection = true;
|
||||
ready(function() {
|
||||
app = new Vue({
|
||||
el: '#app',
|
||||
delimiters: ['[[',']]'],
|
||||
data: {
|
||||
config: {foo:"bar"}
|
||||
},
|
||||
|
||||
var onWebsocketTimeout = function(self) {
|
||||
return function() {
|
||||
console.log('Websocket reconnection timed out, retrying');
|
||||
pendingConnection = false;
|
||||
self.close();
|
||||
self.onclose();
|
||||
};
|
||||
};
|
||||
created: function() {
|
||||
},
|
||||
|
||||
websocketTimeoutId = setTimeout(
|
||||
onWebsocketTimeout(websocket), websocketReconnectMsecs);
|
||||
mounted: function() {
|
||||
},
|
||||
|
||||
websocket.onmessage = function(event) {
|
||||
console.debug(event);
|
||||
for (var listener of eventListeners) {
|
||||
data = event.data;
|
||||
if (typeof event.data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
updated: function() {
|
||||
},
|
||||
|
||||
listener(data);
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onopen = function(event) {
|
||||
if (openedWebsocket) {
|
||||
console.log("There's already an opened websocket connection, closing the newly opened one");
|
||||
this.onclose = function() {};
|
||||
this.close();
|
||||
}
|
||||
|
||||
console.log('Websocket connection successful');
|
||||
openedWebsocket = this;
|
||||
|
||||
if (pendingConnection) {
|
||||
pendingConnection = false;
|
||||
}
|
||||
|
||||
if (websocketTimeoutId) {
|
||||
clearInterval(websocketTimeoutId);
|
||||
websocketTimeoutId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onerror = function(event) {
|
||||
console.error(event);
|
||||
};
|
||||
|
||||
websocket.onclose = function(event) {
|
||||
if (event) {
|
||||
console.log('Websocket closed - code: ' + event.code + ' - reason: ' + event.reason);
|
||||
}
|
||||
|
||||
openedWebsocket = undefined;
|
||||
|
||||
if (!pendingConnection) {
|
||||
pendingConnection = true;
|
||||
initWebsocket();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var initDateTime = function() {
|
||||
var getDateString = function(t) {
|
||||
var s = '';
|
||||
switch (t.getDay()) {
|
||||
case 1: s += 'Mon '; break; case 2: s += 'Tue '; break; case 3: s += 'Wed '; break;
|
||||
case 4: s += 'Thu '; break; case 5: s += 'Fri '; break; case 6: s += 'Sat '; break;
|
||||
case 7: s += 'Sun '; break;
|
||||
}
|
||||
|
||||
s += (t.getDate() < 10 ? '0' : '') + t.getDate() + ' ';
|
||||
switch (t.getMonth()) {
|
||||
case 0: s += 'Jan '; break; case 1: s += 'Feb '; break; case 2: s += 'Mar '; break;
|
||||
case 3: s += 'Apr '; break; case 4: s += 'May '; break; case 5: s += 'Jun '; break;
|
||||
case 6: s += 'Jul '; break; case 7: s += 'Ago '; break; case 8: s += 'Sep '; break;
|
||||
case 9: s += 'Oct '; break; case 10: s += 'Nov '; break; case 11: s += 'Dec '; break;
|
||||
}
|
||||
|
||||
s += t.getFullYear();
|
||||
return s;
|
||||
};
|
||||
|
||||
var setDateTime = function() {
|
||||
var $dateTime = $('#date-time');
|
||||
var $date = $dateTime.find('.date');
|
||||
var $time = $dateTime.find('.time');
|
||||
var now = new Date();
|
||||
|
||||
$date.text(getDateString(now));
|
||||
$time.text((now.getHours() < 10 ? '0' : '') + now.getHours() + ':' +
|
||||
(now.getMinutes() < 10 ? '0' : '') + now.getMinutes());
|
||||
};
|
||||
|
||||
if (dateTimeInterval) {
|
||||
clearInterval(dateTimeInterval);
|
||||
}
|
||||
|
||||
setDateTime();
|
||||
dateTimeInterval = setInterval(setDateTime, 1000);
|
||||
};
|
||||
|
||||
var registerEventListener = function(listener) {
|
||||
eventListeners.push(listener);
|
||||
};
|
||||
|
||||
var initElements = function() {
|
||||
var $tabItems = $('main').find('.plugin-tab-item');
|
||||
var $tabContents = $('main').find('.plugin-tab-content');
|
||||
|
||||
// Set the active tag on the first plugin tab
|
||||
$tabContents.removeClass('active');
|
||||
$tabContents.first().addClass('active');
|
||||
|
||||
$tabItems.removeClass('active');
|
||||
$tabItems.first().addClass('active');
|
||||
|
||||
$tabItems.on('click', function() {
|
||||
var pluginId = $(this).attr('href').substr(1);
|
||||
|
||||
$tabContents.removeClass('active');
|
||||
$tabContents.filter(function(i, content) {
|
||||
return $(content).attr('id') === pluginId
|
||||
}).addClass('active');
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
var initModalOpenBindings = function() {
|
||||
$('body').on('mouseup touchend', '[data-modal]', function(event) {
|
||||
var $source = $(this);
|
||||
var $modal = $($source.data('modal'));
|
||||
$modal.height($(document).height() + 2);
|
||||
|
||||
var $container = $modal.find('.modal-container');
|
||||
var top = 40 + $(window).scrollTop();
|
||||
$container.css('margin-top', top + 'px');
|
||||
|
||||
modalFadingInTimeoutId = setTimeout(function() {
|
||||
modalFadingInTimeoutId = undefined;
|
||||
}, 100);
|
||||
|
||||
$modal.fadeIn();
|
||||
});
|
||||
};
|
||||
|
||||
var initModalCloseBindings = function() {
|
||||
$('body').on('mouseup touchend', '[data-dismiss-modal]', function(event) {
|
||||
var $source = $(this);
|
||||
var $modal = $($source.data('dismiss-modal'));
|
||||
$modal.fadeOut();
|
||||
});
|
||||
|
||||
$('body').on('mouseup touchend', function(event) {
|
||||
var $source = $(event.target);
|
||||
if (!$source.parents('.modal').length
|
||||
&& !$source.data('modal')
|
||||
&& !$source.data('dismiss-modal')) {
|
||||
if (!modalFadingInTimeoutId) {
|
||||
$('.modal').filter(':visible').fadeOut();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var initPanelOpenBindings = function() {
|
||||
$('body').on('mouseup touchend', '[data-panel]', function(event) {
|
||||
var $source = $(this);
|
||||
var $panel = $($source.data('panel'));
|
||||
setTimeout(() => {
|
||||
$panel.show();
|
||||
}, 200);
|
||||
});
|
||||
};
|
||||
|
||||
var initPanelCloseBindings = function() {
|
||||
$('body').on('mouseup touchend', function(event) {
|
||||
var $source = $(event.target);
|
||||
if ($source.data('panel') || $source.parents('[data-panel]').length) {
|
||||
var $panel = $source.data('panel') ? $($source.data('panel')) :
|
||||
$($source.parents('[data-panel]').data('panel'));
|
||||
$panel.toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$source.parents('.panel').length
|
||||
&& !$source.data('panel')) {
|
||||
$('.panel').filter(':visible').hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var initModals = function() {
|
||||
initModalOpenBindings();
|
||||
initModalCloseBindings();
|
||||
};
|
||||
|
||||
var initPanels = function() {
|
||||
initPanelOpenBindings();
|
||||
initPanelCloseBindings();
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
initWebsocket();
|
||||
initElements();
|
||||
initDateTime();
|
||||
initModals();
|
||||
initPanels();
|
||||
};
|
||||
|
||||
window.registerEventListener = registerEventListener;
|
||||
init();
|
||||
destroyed: function() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
function execute(request, onSuccess, onError, onComplete) {
|
||||
request['target'] = 'localhost';
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url: '/execute',
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify(request),
|
||||
complete: function() {
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
if (onError) {
|
||||
onError(xhr, status, error);
|
||||
}
|
||||
},
|
||||
success: function(response, status, xhr) {
|
||||
if ('errors' in response && response.errors.length) {
|
||||
if (onError) {
|
||||
onError(xhr, '500', response.errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess(response, status, xhr);
|
||||
}
|
||||
},
|
||||
beforeSend: function(xhr) {
|
||||
if (window.token) {
|
||||
xhr.setRequestHeader('X-Token', window.token);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function run(request) {
|
||||
request['type'] = 'request';
|
||||
return new Promise((resolve, reject) => {
|
||||
execute(request,
|
||||
onSuccess = (response) => {
|
||||
resolve(response);
|
||||
},
|
||||
|
||||
onError = (xhr, status, error) => {
|
||||
reject(xhr, status, error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createNotification(options) {
|
||||
var $notificationContainer = $('#notification-container');
|
||||
var $notification = $('<div></div>').addClass('notification');
|
||||
var $title = $('<div></div>').addClass('notification-title');
|
||||
var timeout = 'timeout' in options ? options.timeout : 10000;
|
||||
|
||||
if ('title' in options) {
|
||||
$title.text(options.title);
|
||||
}
|
||||
|
||||
var $body = $('<div></div>').addClass('notification-body');
|
||||
var $imgDiv = $('<div></div>').addClass('notification-image').addClass('three columns');
|
||||
var $img = $('<i></i>').addClass('fa fa-bell').addClass('three columns').addClass('notification-image-item');
|
||||
var $text = $('<div></div>').addClass('notification-text').addClass('nine columns')
|
||||
|
||||
if ('image' in options) {
|
||||
$img = $('<img></img>').attr('src', options.image);
|
||||
} else if ('icon' in options) {
|
||||
$img.removeClass('fa-bell').addClass('fa-' + options.icon);
|
||||
}
|
||||
|
||||
if ('text' in options) {
|
||||
$text.text(options.text);
|
||||
} else if ('html' in options) {
|
||||
$text.html(options.html);
|
||||
}
|
||||
|
||||
$img.appendTo($imgDiv);
|
||||
$imgDiv.appendTo($body);
|
||||
$text.appendTo($body);
|
||||
|
||||
var clickHandler;
|
||||
var removeTimeoutId;
|
||||
|
||||
var removeNotification = function() {
|
||||
$notification.fadeOut(300, function() {
|
||||
$notification.remove();
|
||||
});
|
||||
};
|
||||
|
||||
var setRemoveTimeout = function() {
|
||||
if (timeout) {
|
||||
removeTimeoutId = setTimeout(removeNotification, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
setRemoveTimeout();
|
||||
|
||||
if ('onclick' in options) {
|
||||
clickHandler = options.onclick;
|
||||
} else {
|
||||
clickHandler = removeNotification;
|
||||
}
|
||||
|
||||
$notification.on('click', clickHandler);
|
||||
$notification.hover(
|
||||
// on hover start
|
||||
function() {
|
||||
if (removeTimeoutId) {
|
||||
clearTimeout(removeTimeoutId);
|
||||
removeTimeoutId = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
// on hover end
|
||||
function() {
|
||||
if (timeout) {
|
||||
setRemoveTimeout();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$title.appendTo($notification);
|
||||
$body.appendTo($notification);
|
||||
$notification.prependTo($notificationContainer);
|
||||
$notification.fadeIn();
|
||||
}
|
||||
|
||||
function showError(errorMessage) {
|
||||
createNotification({
|
||||
'icon': 'exclamation',
|
||||
'text': errorMessage,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
372
platypush/backend/http/static/js/application_old.js
Normal file
372
platypush/backend/http/static/js/application_old.js
Normal file
|
@ -0,0 +1,372 @@
|
|||
$(document).ready(function() {
|
||||
var websocket,
|
||||
pendingConnection = false,
|
||||
openedWebsocket,
|
||||
dateTimeInterval,
|
||||
websocketTimeoutId,
|
||||
modalFadingInTimeoutId,
|
||||
websocketReconnectMsecs = 30000,
|
||||
eventListeners = [];
|
||||
|
||||
var initWebsocket = function() {
|
||||
try {
|
||||
url_prefix = window.has_ssl ? 'wss://' : 'ws://';
|
||||
websocket = new WebSocket(url_prefix + window.location.hostname + ':' + window.websocket_port);
|
||||
} catch (err) {
|
||||
websocket = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
pendingConnection = true;
|
||||
|
||||
var onWebsocketTimeout = function(self) {
|
||||
return function() {
|
||||
console.log('Websocket reconnection timed out, retrying');
|
||||
pendingConnection = false;
|
||||
self.close();
|
||||
self.onclose();
|
||||
};
|
||||
};
|
||||
|
||||
websocketTimeoutId = setTimeout(
|
||||
onWebsocketTimeout(websocket), websocketReconnectMsecs);
|
||||
|
||||
websocket.onmessage = function(event) {
|
||||
console.debug(event);
|
||||
for (var listener of eventListeners) {
|
||||
data = event.data;
|
||||
if (typeof event.data === 'string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
listener(data);
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onopen = function(event) {
|
||||
if (openedWebsocket) {
|
||||
console.log("There's already an opened websocket connection, closing the newly opened one");
|
||||
this.onclose = function() {};
|
||||
this.close();
|
||||
}
|
||||
|
||||
console.log('Websocket connection successful');
|
||||
openedWebsocket = this;
|
||||
|
||||
if (pendingConnection) {
|
||||
pendingConnection = false;
|
||||
}
|
||||
|
||||
if (websocketTimeoutId) {
|
||||
clearInterval(websocketTimeoutId);
|
||||
websocketTimeoutId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onerror = function(event) {
|
||||
console.error(event);
|
||||
};
|
||||
|
||||
websocket.onclose = function(event) {
|
||||
if (event) {
|
||||
console.log('Websocket closed - code: ' + event.code + ' - reason: ' + event.reason);
|
||||
}
|
||||
|
||||
openedWebsocket = undefined;
|
||||
|
||||
if (!pendingConnection) {
|
||||
pendingConnection = true;
|
||||
initWebsocket();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var initDateTime = function() {
|
||||
var getDateString = function(t) {
|
||||
var s = '';
|
||||
switch (t.getDay()) {
|
||||
case 1: s += 'Mon '; break; case 2: s += 'Tue '; break; case 3: s += 'Wed '; break;
|
||||
case 4: s += 'Thu '; break; case 5: s += 'Fri '; break; case 6: s += 'Sat '; break;
|
||||
case 7: s += 'Sun '; break;
|
||||
}
|
||||
|
||||
s += (t.getDate() < 10 ? '0' : '') + t.getDate() + ' ';
|
||||
switch (t.getMonth()) {
|
||||
case 0: s += 'Jan '; break; case 1: s += 'Feb '; break; case 2: s += 'Mar '; break;
|
||||
case 3: s += 'Apr '; break; case 4: s += 'May '; break; case 5: s += 'Jun '; break;
|
||||
case 6: s += 'Jul '; break; case 7: s += 'Ago '; break; case 8: s += 'Sep '; break;
|
||||
case 9: s += 'Oct '; break; case 10: s += 'Nov '; break; case 11: s += 'Dec '; break;
|
||||
}
|
||||
|
||||
s += t.getFullYear();
|
||||
return s;
|
||||
};
|
||||
|
||||
var setDateTime = function() {
|
||||
var $dateTime = $('#date-time');
|
||||
var $date = $dateTime.find('.date');
|
||||
var $time = $dateTime.find('.time');
|
||||
var now = new Date();
|
||||
|
||||
$date.text(getDateString(now));
|
||||
$time.text((now.getHours() < 10 ? '0' : '') + now.getHours() + ':' +
|
||||
(now.getMinutes() < 10 ? '0' : '') + now.getMinutes());
|
||||
};
|
||||
|
||||
if (dateTimeInterval) {
|
||||
clearInterval(dateTimeInterval);
|
||||
}
|
||||
|
||||
setDateTime();
|
||||
dateTimeInterval = setInterval(setDateTime, 1000);
|
||||
};
|
||||
|
||||
var registerEventListener = function(listener) {
|
||||
eventListeners.push(listener);
|
||||
};
|
||||
|
||||
var initElements = function() {
|
||||
var $tabItems = $('main').find('.plugin-tab-item');
|
||||
var $tabContents = $('main').find('.plugin-tab-content');
|
||||
|
||||
// Set the active tag on the first plugin tab
|
||||
$tabContents.removeClass('active');
|
||||
$tabContents.first().addClass('active');
|
||||
|
||||
$tabItems.removeClass('active');
|
||||
$tabItems.first().addClass('active');
|
||||
|
||||
$tabItems.on('click', function() {
|
||||
var pluginId = $(this).attr('href').substr(1);
|
||||
|
||||
$tabContents.removeClass('active');
|
||||
$tabContents.filter(function(i, content) {
|
||||
return $(content).attr('id') === pluginId
|
||||
}).addClass('active');
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
var initModalOpenBindings = function() {
|
||||
$('body').on('mouseup touchend', '[data-modal]', function(event) {
|
||||
var $source = $(this);
|
||||
var $modal = $($source.data('modal'));
|
||||
$modal.height($(document).height() + 2);
|
||||
|
||||
var $container = $modal.find('.modal-container');
|
||||
var top = 40 + $(window).scrollTop();
|
||||
$container.css('margin-top', top + 'px');
|
||||
|
||||
modalFadingInTimeoutId = setTimeout(function() {
|
||||
modalFadingInTimeoutId = undefined;
|
||||
}, 100);
|
||||
|
||||
$modal.fadeIn();
|
||||
});
|
||||
};
|
||||
|
||||
var initModalCloseBindings = function() {
|
||||
$('body').on('mouseup touchend', '[data-dismiss-modal]', function(event) {
|
||||
var $source = $(this);
|
||||
var $modal = $($source.data('dismiss-modal'));
|
||||
$modal.fadeOut();
|
||||
});
|
||||
|
||||
$('body').on('mouseup touchend', function(event) {
|
||||
var $source = $(event.target);
|
||||
if (!$source.parents('.modal').length
|
||||
&& !$source.data('modal')
|
||||
&& !$source.data('dismiss-modal')) {
|
||||
if (!modalFadingInTimeoutId) {
|
||||
$('.modal').filter(':visible').fadeOut();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var initPanelOpenBindings = function() {
|
||||
$('body').on('mouseup touchend', '[data-panel]', function(event) {
|
||||
var $source = $(this);
|
||||
var $panel = $($source.data('panel'));
|
||||
setTimeout(() => {
|
||||
$panel.show();
|
||||
}, 200);
|
||||
});
|
||||
};
|
||||
|
||||
var initPanelCloseBindings = function() {
|
||||
$('body').on('mouseup touchend', function(event) {
|
||||
var $source = $(event.target);
|
||||
if ($source.data('panel') || $source.parents('[data-panel]').length) {
|
||||
var $panel = $source.data('panel') ? $($source.data('panel')) :
|
||||
$($source.parents('[data-panel]').data('panel'));
|
||||
$panel.toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$source.parents('.panel').length
|
||||
&& !$source.data('panel')) {
|
||||
$('.panel').filter(':visible').hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var initModals = function() {
|
||||
initModalOpenBindings();
|
||||
initModalCloseBindings();
|
||||
};
|
||||
|
||||
var initPanels = function() {
|
||||
initPanelOpenBindings();
|
||||
initPanelCloseBindings();
|
||||
};
|
||||
|
||||
var init = function() {
|
||||
initWebsocket();
|
||||
initElements();
|
||||
initDateTime();
|
||||
initModals();
|
||||
initPanels();
|
||||
};
|
||||
|
||||
window.registerEventListener = registerEventListener;
|
||||
init();
|
||||
});
|
||||
|
||||
function execute(request, onSuccess, onError, onComplete) {
|
||||
request['target'] = 'localhost';
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url: '/execute',
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify(request),
|
||||
complete: function() {
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
if (onError) {
|
||||
onError(xhr, status, error);
|
||||
}
|
||||
},
|
||||
success: function(response, status, xhr) {
|
||||
if ('errors' in response && response.errors.length) {
|
||||
if (onError) {
|
||||
onError(xhr, '500', response.errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess(response, status, xhr);
|
||||
}
|
||||
},
|
||||
beforeSend: function(xhr) {
|
||||
if (window.token) {
|
||||
xhr.setRequestHeader('X-Token', window.token);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function run(request) {
|
||||
request['type'] = 'request';
|
||||
return new Promise((resolve, reject) => {
|
||||
execute(request,
|
||||
onSuccess = (response) => {
|
||||
resolve(response);
|
||||
},
|
||||
|
||||
onError = (xhr, status, error) => {
|
||||
reject(xhr, status, error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function createNotification(options) {
|
||||
var $notificationContainer = $('#notification-container');
|
||||
var $notification = $('<div></div>').addClass('notification');
|
||||
var $title = $('<div></div>').addClass('notification-title');
|
||||
var timeout = 'timeout' in options ? options.timeout : 10000;
|
||||
|
||||
if ('title' in options) {
|
||||
$title.text(options.title);
|
||||
}
|
||||
|
||||
var $body = $('<div></div>').addClass('notification-body');
|
||||
var $imgDiv = $('<div></div>').addClass('notification-image').addClass('three columns');
|
||||
var $img = $('<i></i>').addClass('fa fa-bell').addClass('three columns').addClass('notification-image-item');
|
||||
var $text = $('<div></div>').addClass('notification-text').addClass('nine columns')
|
||||
|
||||
if ('image' in options) {
|
||||
$img = $('<img></img>').attr('src', options.image);
|
||||
} else if ('icon' in options) {
|
||||
$img.removeClass('fa-bell').addClass('fa-' + options.icon);
|
||||
}
|
||||
|
||||
if ('text' in options) {
|
||||
$text.text(options.text);
|
||||
} else if ('html' in options) {
|
||||
$text.html(options.html);
|
||||
}
|
||||
|
||||
$img.appendTo($imgDiv);
|
||||
$imgDiv.appendTo($body);
|
||||
$text.appendTo($body);
|
||||
|
||||
var clickHandler;
|
||||
var removeTimeoutId;
|
||||
|
||||
var removeNotification = function() {
|
||||
$notification.fadeOut(300, function() {
|
||||
$notification.remove();
|
||||
});
|
||||
};
|
||||
|
||||
var setRemoveTimeout = function() {
|
||||
if (timeout) {
|
||||
removeTimeoutId = setTimeout(removeNotification, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
setRemoveTimeout();
|
||||
|
||||
if ('onclick' in options) {
|
||||
clickHandler = options.onclick;
|
||||
} else {
|
||||
clickHandler = removeNotification;
|
||||
}
|
||||
|
||||
$notification.on('click', clickHandler);
|
||||
$notification.hover(
|
||||
// on hover start
|
||||
function() {
|
||||
if (removeTimeoutId) {
|
||||
clearTimeout(removeTimeoutId);
|
||||
removeTimeoutId = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
// on hover end
|
||||
function() {
|
||||
if (timeout) {
|
||||
setRemoveTimeout();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$title.appendTo($notification);
|
||||
$body.appendTo($notification);
|
||||
$notification.prependTo($notificationContainer);
|
||||
$notification.fadeIn();
|
||||
}
|
||||
|
||||
function showError(errorMessage) {
|
||||
createNotification({
|
||||
'icon': 'exclamation',
|
||||
'text': errorMessage,
|
||||
});
|
||||
}
|
||||
|
11944
platypush/backend/http/static/js/vue.js
Normal file
11944
platypush/backend/http/static/js/vue.js
Normal file
File diff suppressed because it is too large
Load diff
6
platypush/backend/http/static/js/vue.min.js
vendored
Normal file
6
platypush/backend/http/static/js/vue.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -3,26 +3,36 @@
|
|||
<title>Platypush Web Console</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton-tabs.css') }}">
|
||||
<!-- <link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton-tabs.css') }}"> -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='font-awesome/css/font-awesome.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/application.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/toggles.css') }}">
|
||||
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.12.1.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/vue.js') }}"></script>
|
||||
<!--<script type="text/javascript" src="{{ url_for('static', filename='js/vue.min.js') }}"></script>-->
|
||||
|
||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script> -->
|
||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.12.1.min.js') }}"></script> -->
|
||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script> -->
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script>
|
||||
<!-- <script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script> -->
|
||||
<script type="text/javascript">
|
||||
window.websocket_port = {% print(websocket_port) %}
|
||||
window.has_ssl = {% print('true' if has_ssl else 'false') %};
|
||||
if (!window.config) {
|
||||
window.config = {};
|
||||
}
|
||||
|
||||
window.config = { ...window.config,
|
||||
websocket_port: {% print(websocket_port) %},
|
||||
has_ssl: {% print('true' if has_ssl else 'false') %},
|
||||
plugins: JSON.parse('{% print(utils.to_json(plugins))|safe %}'),
|
||||
};
|
||||
|
||||
{% if token %}
|
||||
window.token = '{% print(token) %}'
|
||||
window.config.token = '{% print(token) %}';
|
||||
{% else %}
|
||||
window.token = undefined
|
||||
window.config.token = undefined;
|
||||
{% endif %}
|
||||
</script>
|
||||
</head>
|
||||
|
@ -42,37 +52,24 @@
|
|||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<ul class="tab-nav">
|
||||
{% for plugin in plugins.keys()|sort() %}
|
||||
<li>
|
||||
<a class="button plugin-tab-item" href="#{% print plugin %}">
|
||||
{% print plugin %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<nav>
|
||||
{% for plugin in plugins.keys()|sort() %}
|
||||
<a href="#{% print plugin %}">
|
||||
{% print plugin %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
<div class="tab-content">
|
||||
<main>
|
||||
<div id="app">
|
||||
{% for plugin in plugins.keys()|sort() %}
|
||||
{% with configuration=plugins[plugin], utils=utils %}
|
||||
<div class="tab-pane plugin-tab-content" id="{% print plugin %}">
|
||||
{% include 'plugins/' + plugin + '.html' %}
|
||||
<div class="tab-pane plugin-tab-content" id="{% print plugin %}-container">
|
||||
{% include 'panel/' + plugin + '/index.html' %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div id="notification-container"></div>
|
||||
|
||||
<div id="hidden-plugins-container">
|
||||
{% for plugin in hidden_plugins.keys()|sort() %}
|
||||
{% set configuration = plugins[plugin] %}
|
||||
<div class="plugin" id="{% print plugin %}">
|
||||
{% include 'plugins/' + plugin + '.html' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</main>
|
||||
<body>
|
||||
</body>
|
||||
|
||||
|
|
78
platypush/backend/http/templates/index_old.html
Normal file
78
platypush/backend/http/templates/index_old.html
Normal file
|
@ -0,0 +1,78 @@
|
|||
<!doctype html>
|
||||
<head>
|
||||
<title>Platypush Web Console</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/skeleton-tabs.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/normalize.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='font-awesome/css/font-awesome.min.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/application.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/toggles.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/jquery-ui.css') }}">
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.3.1.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-ui-1.12.1.min.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/skeleton-tabs.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/pushbullet.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
window.websocket_port = {% print(websocket_port) %};
|
||||
window.has_ssl = {% print('true' if has_ssl else 'false') %};
|
||||
|
||||
{% if token %}
|
||||
window.token = '{% print(token) %}'
|
||||
{% else %}
|
||||
window.token = undefined
|
||||
{% endif %}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="row">
|
||||
<div class="logo nine columns">
|
||||
<span class="logo-1">Platypush</span>
|
||||
<span class="logo-2">Web Panel</span>
|
||||
</div>
|
||||
|
||||
<div id="date-time" class="three columns">
|
||||
<div class="date"></div>
|
||||
<div class="time"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<ul class="tab-nav">
|
||||
{% for plugin in plugins.keys()|sort() %}
|
||||
<li>
|
||||
<a class="button plugin-tab-item" href="#{% print plugin %}">
|
||||
{% print plugin %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
{% for plugin in plugins.keys()|sort() %}
|
||||
{% with configuration=plugins[plugin], utils=utils %}
|
||||
<div class="tab-pane plugin-tab-content" id="{% print plugin %}">
|
||||
{% include 'plugins/' + plugin + '.html' %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div id="notification-container"></div>
|
||||
|
||||
<div id="hidden-plugins-container">
|
||||
{% for plugin in hidden_plugins.keys()|sort() %}
|
||||
{% set configuration = plugins[plugin] %}
|
||||
<div class="plugin" id="{% print plugin %}">
|
||||
{% include 'plugins/' + plugin + '.html' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
IT WORKED!
|
||||
|
|
@ -83,6 +83,9 @@ class HttpUtils(object):
|
|||
|
||||
@classmethod
|
||||
def to_json(cls, data):
|
||||
if isinstance(data, type({}.keys())):
|
||||
# Convert dict_keys to list before serializing
|
||||
data = list(data)
|
||||
return json.dumps(data)
|
||||
|
||||
@classmethod
|
||||
|
|
Loading…
Reference in a new issue