forked from platypush/platypush
Support for multiple resource_dirs on the HTTP backend
It is now possible to map multiple static resource directories to the Flask HTTP server
This commit is contained in:
parent
9e28379203
commit
62045c2b5c
2 changed files with 62 additions and 10 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit 642eb7030cfc599b62fcf304bc94e4d84fecde8c
|
Subproject commit 6c0e65ccfe020bf6ce2eca43c387e827d4764c12
|
|
@ -62,7 +62,7 @@ class HttpBackend(Backend):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, port=8008, websocket_port=8009, disable_websocket=False,
|
def __init__(self, port=8008, websocket_port=8009, disable_websocket=False,
|
||||||
redis_queue='platypush/http', dashboard={},
|
redis_queue='platypush/http', dashboard={}, resource_dirs={},
|
||||||
ssl_cert=None, ssl_key=None, ssl_cafile=None, ssl_capath=None,
|
ssl_cert=None, ssl_key=None, ssl_cafile=None, ssl_capath=None,
|
||||||
maps={}, **kwargs):
|
maps={}, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -90,6 +90,12 @@ class HttpBackend(Backend):
|
||||||
: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
|
:type ssl_capath: str
|
||||||
|
|
||||||
|
:param resource_dirs: Static resources directories that will be
|
||||||
|
accessible through ``/resources/<path>``. It is expressed as a map
|
||||||
|
where the key is the relative path under ``/resources`` to expose and
|
||||||
|
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::
|
Example configuration::
|
||||||
|
@ -105,7 +111,7 @@ class HttpBackend(Backend):
|
||||||
columns: 3
|
columns: 3
|
||||||
image-carousel: # Image carousel
|
image-carousel: # Image carousel
|
||||||
columns: 6
|
columns: 6
|
||||||
images_path: /static/resources/Dropbox/Photos/carousel # Path (relative to ``platypush/backend/http``) containing the carousel pictures
|
images_path: ~/Dropbox/Photos/carousel # Absolute path (valid as long as it's a subdirectory of one of the available `resource_dirs`)
|
||||||
refresh_seconds: 15
|
refresh_seconds: 15
|
||||||
rss-news: # RSS feeds widget
|
rss-news: # RSS feeds widget
|
||||||
# Requires backend.http.poll to be enabled with some RSS sources and write them to sqlite db
|
# Requires backend.http.poll to be enabled with some RSS sources and write them to sqlite db
|
||||||
|
@ -128,6 +134,8 @@ class HttpBackend(Backend):
|
||||||
self.websocket_thread = None
|
self.websocket_thread = None
|
||||||
self.redis_thread = None
|
self.redis_thread = None
|
||||||
self.redis = None
|
self.redis = None
|
||||||
|
self.resource_dirs = { name: os.path.abspath(
|
||||||
|
os.path.expanduser(d)) for name, d in resource_dirs.items() }
|
||||||
self.active_websockets = set()
|
self.active_websockets = set()
|
||||||
self.ssl_context = get_ssl_server_context(ssl_cert=ssl_cert,
|
self.ssl_context = get_ssl_server_context(ssl_cert=ssl_cert,
|
||||||
ssl_key=ssl_key,
|
ssl_key=ssl_key,
|
||||||
|
@ -226,7 +234,6 @@ class HttpBackend(Backend):
|
||||||
""" Web server main process """
|
""" Web server main process """
|
||||||
basedir = os.path.dirname(inspect.getfile(self.__class__))
|
basedir = os.path.dirname(inspect.getfile(self.__class__))
|
||||||
template_dir = os.path.join(basedir, 'templates')
|
template_dir = os.path.join(basedir, 'templates')
|
||||||
static_dir = os.path.join(basedir, 'static')
|
|
||||||
app = Flask(__name__, template_folder=template_dir)
|
app = Flask(__name__, template_folder=template_dir)
|
||||||
self.redis_thread = Thread(target=self.redis_poll)
|
self.redis_thread = Thread(target=self.redis_poll)
|
||||||
self.redis_thread.start()
|
self.redis_thread.start()
|
||||||
|
@ -288,10 +295,36 @@ class HttpBackend(Backend):
|
||||||
redis.rpush(self.redis_queue, str(event))
|
redis.rpush(self.redis_queue, str(event))
|
||||||
return jsonify({ 'status': 'ok' })
|
return jsonify({ 'status': 'ok' })
|
||||||
|
|
||||||
@app.route('/static/<path>', methods=['GET'])
|
@app.route('/resources/<path:path>', methods=['GET'])
|
||||||
def static_path(path):
|
def static_path(path):
|
||||||
""" Static resources """
|
""" Static resources """
|
||||||
return send_from_directory(static_dir, filename)
|
base_path = os.path.dirname(path).split('/')
|
||||||
|
while base_path:
|
||||||
|
if os.sep.join(base_path) in self.resource_dirs:
|
||||||
|
break
|
||||||
|
base_path.pop()
|
||||||
|
|
||||||
|
if not base_path:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
base_path = os.sep.join(base_path)
|
||||||
|
real_base_path = self.resource_dirs[base_path]
|
||||||
|
real_path = real_base_path
|
||||||
|
|
||||||
|
file_path = [s for s in
|
||||||
|
re.sub(r'^{}(.*)$'.format(base_path), '\\1', path) \
|
||||||
|
.split('/') if s]
|
||||||
|
|
||||||
|
for p in file_path[:-1]:
|
||||||
|
real_path += os.sep + p
|
||||||
|
file_path.pop(0)
|
||||||
|
|
||||||
|
file_path = file_path.pop(0)
|
||||||
|
if not real_path.startswith(real_base_path):
|
||||||
|
# Directory climbing attempt
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
return send_from_directory(real_path, file_path)
|
||||||
|
|
||||||
@app.route('/dashboard', methods=['GET'])
|
@app.route('/dashboard', methods=['GET'])
|
||||||
def dashboard():
|
def dashboard():
|
||||||
|
@ -482,10 +515,29 @@ class HttpUtils(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_web_directory(cls, directory, *extensions):
|
def search_web_directory(cls, directory, *extensions):
|
||||||
directory = re.sub('^/+', '', directory)
|
directory = os.path.abspath(os.path.expanduser(directory))
|
||||||
basedir = os.path.dirname(inspect.getfile(cls))
|
resource_dirs = get_backend('http').resource_dirs
|
||||||
results = cls.search_directory(os.path.join(basedir, directory), *extensions)
|
resource_path = None
|
||||||
return [item[len(basedir):] for item in results]
|
uri = ''
|
||||||
|
|
||||||
|
for name, resource_path in resource_dirs.items():
|
||||||
|
if directory.startswith(resource_path):
|
||||||
|
subdir = re.sub('^{}(.*)$'.format(resource_path),
|
||||||
|
'\\1', directory)
|
||||||
|
uri = '/resources/' + name
|
||||||
|
break
|
||||||
|
|
||||||
|
if not uri:
|
||||||
|
raise RuntimeError('Directory {} not found among the available ' +
|
||||||
|
'static resources on the webserver'.format(
|
||||||
|
directory))
|
||||||
|
|
||||||
|
results = [
|
||||||
|
re.sub('^{}(.*)$'.format(resource_path), uri + '\\1', path)
|
||||||
|
for path in cls.search_directory(directory, *extensions)
|
||||||
|
]
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_json(cls, data):
|
def to_json(cls, data):
|
||||||
|
|
Loading…
Reference in a new issue