import json import sys from flask import Blueprint, request, send_from_directory from importlib import import_module from modules import socketio, cbpi from git import Repo import os import requests import yaml import shutil import imp blueprint = Blueprint('addon', __name__) modules = {} def merge(source, destination): """ Helper method to merge two dicts :param source: :param destination: :return: """ for key, value in list(source.items()): if isinstance(value, dict): # get node or create one node = destination.setdefault(key, {}) merge(value, node) else: destination[key] = value return destination @blueprint.route('/', methods=['GET']) def getPlugins(): """ Endpoint for all plugins :return: """ result = [] for filename in os.listdir("./modules/plugins"): if filename.endswith(".DS_Store") or filename.endswith(".py") or filename.endswith(".pyc"): continue result.append(filename) return json.dumps(result) @blueprint.route('/', methods=['GET']) def getFile(name): """ Returns plugin code :param name: plugin name :return: the plugin code from __init__.py """ return send_from_directory('./plugins/'+name, "__init__.py") @blueprint.route('/', methods=['PUT']) def createPlugin(name): """ Create a new plugin file :param name: the plugin name :return: empty http response 204 """ if not os.path.exists("./modules/plugins/"+name): os.makedirs("./modules/plugins/"+name) with open("./modules/plugins/" + name + "/__init__.py", "wb") as fo: fo.write("") cbpi.emit_message("PLUGIN %s CREATED" % (name)) return ('', 204) else: cbpi.emit_message("Failed to create plugin %s. Name arlready in use" % (name)) return ('', 500) @blueprint.route('/', methods=['POST']) def saveFile(name): """ save plugin code. code is provides via http body :param name: the plugin name :return: empty http reponse """ with open("./modules/plugins/"+name+"/__init__.py", "wb") as fo: fo.write(request.get_data()) cbpi.emit_message("PLUGIN %s SAVED" % (name)) return ('', 204) @blueprint.route('/', methods=['DELETE']) def deletePlugin(name): """ Delete plugin :param name: plugin name :return: HTTP 204 if ok - HTTP 500 if plugin not exists """ if os.path.isdir("./modules/plugins/"+name) is False: return ('Dir Not found', 500) shutil.rmtree("./modules/plugins/"+name) cbpi.notify("Plugin deleted", "Plugin %s deleted successfully" % name) return ('', 204) @blueprint.route('//reload/', methods=['POST']) def reload(name): """ hot reload plugnin :param name: :return: """ try: if name in cache["modules"]: imp.reload(cache["modules"][name]) cbpi.emit_message("REALOD OF PLUGIN %s SUCCESSFUL" % (name)) return ('', 204) else: cache["modules"][name] = import_module("modules.plugins.%s" % (name)) return ('', 204) except Exception as e: cbpi.emit_message("REALOD OF PLUGIN %s FAILED" % (name)) return json.dumps(e.message) @blueprint.route('/list', methods=['GET']) def plugins(): """ Read the central plugin yaml to get a list of all official plugins :return: """ response = requests.get("https://raw.githubusercontent.com/jpgimenez/craftbeerpi-plugins/master/plugins.yaml") cbpi.cache["plugins"] = merge(yaml.safe_load(response.text), cbpi.cache["plugins"]) for key, value in cbpi.cache["plugins"].items(): value["installed"] = os.path.isdir("./modules/plugins/%s/" % (key)) return json.dumps(cbpi.cache["plugins"]) @blueprint.route('//download', methods=['POST']) def download_addon(name): plugin = cbpi.cache["plugins"].get(name) if plugin is None: return ('', 404) plugin["loading"] = True try: Repo.clone_from(plugin.get("repo_url"), "./modules/plugins/%s/" % (name)) cbpi.notify("Download successful", "Plugin %s downloaded successfully" % name) finally: plugin["loading"] = False return ('', 204) @blueprint.route('//update', methods=['POST']) def update_addon(name): """ Updates a addon :param name: plugin name :return: HTTP 204 if ok - HTTP 500 if plugin not exists """ plugin = cbpi.cache["plugins"].get(name) if plugin is None: return ('', 404) plugin["loading"] = True repo = Repo("./modules/plugins/%s/" % (name)) if repo.remotes.origin.url == plugin.get('repo_url'): o = repo.remotes.origin _info = o.pull() else: # url has changed the plugin needs to be re-downloaded deletePlugin(name) return download_addon(name) reload(name) plugin["loading"] = False cbpi.notify("Plugin Updated", "Plugin %s updated successfully. Please restart the system" % name) return ('', 204) def loadCorePlugins(): for filename in os.listdir("./modules/base_plugins"): if os.path.isdir("./modules/base_plugins/"+filename) is False: continue try: modules[filename] = import_module("modules.base_plugins.%s" % (filename)) except Exception as e: cbpi.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None) cbpi.app.logger.error(e) def loadPlugins(): for filename in os.listdir("./modules/plugins"): if os.path.isdir("./modules/plugins/" + filename) is False: continue try: modules[filename] = import_module("modules.plugins.%s" % (filename)) except Exception as e: cbpi.notify("Failed to load plugin %s " % filename, str(e), type="danger", timeout=None) cbpi.app.logger.error(e) #@cbpi.initalizer(order=1) def initPlugins(): loadCorePlugins() loadPlugins() @cbpi.initalizer(order=2) def init(cbpi): cbpi.app.register_blueprint(blueprint, url_prefix='/api/editor')