Made the UI independent + update job download plugins
This commit is contained in:
parent
44ce5381c2
commit
e6cb5b0b09
|
@ -1,14 +1,14 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from io import BytesIO
|
||||
from os import getenv, listdir, makedirs, chmod, stat, _exit
|
||||
from os.path import isfile, dirname
|
||||
from os import getenv, listdir, makedirs, chmod, stat, _exit, walk
|
||||
from os.path import join, isfile, dirname
|
||||
from stat import S_IEXEC
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from uuid import uuid4
|
||||
from glob import glob
|
||||
from json import load, loads
|
||||
from shutil import copytree, rmtree
|
||||
from shutil import chown, copytree, rmtree
|
||||
from traceback import format_exc
|
||||
from zipfile import ZipFile
|
||||
|
||||
|
@ -97,6 +97,7 @@ try:
|
|||
continue
|
||||
|
||||
external_plugins = []
|
||||
external_plugins_ids = []
|
||||
for plugin in listdir("/etc/bunkerweb/plugins"):
|
||||
with open(
|
||||
f"/etc/bunkerweb/plugins/{plugin}/plugin.json",
|
||||
|
@ -105,6 +106,18 @@ try:
|
|||
plugin_file = load(f)
|
||||
|
||||
external_plugins.append(plugin_file)
|
||||
external_plugins_ids.append(plugin_file["id"])
|
||||
|
||||
db_plugins = db.get_plugins()
|
||||
for plugin in db_plugins:
|
||||
if plugin["external"] is True and plugin["id"] not in external_plugins_ids:
|
||||
external_plugins.append(plugin)
|
||||
|
||||
# Fix permissions for the certificates
|
||||
for root, dirs, files in walk("/data/plugins", topdown=False):
|
||||
for name in files + dirs:
|
||||
chown(join(root, name), "root", 101)
|
||||
chmod(join(root, name), 0o770)
|
||||
|
||||
if external_plugins:
|
||||
err = db.update_external_plugins(external_plugins)
|
||||
|
|
|
@ -1111,10 +1111,14 @@ class Database:
|
|||
Jobs.name == job["name"]
|
||||
).update(updates)
|
||||
|
||||
if exists(f"/usr/share/bunkerweb/core/{plugin['id']}/ui"):
|
||||
if {"template.html", "actions.py"}.issubset(
|
||||
listdir(f"/usr/share/bunkerweb/core/{plugin['id']}/ui")
|
||||
):
|
||||
path_ui = (
|
||||
f"/var/tmp/bunkerweb/ui/{plugin['id']}/ui"
|
||||
if exists(f"/var/tmp/bunkerweb/ui/{plugin['id']}/ui")
|
||||
else f"/etc/bunkerweb/plugins/{plugin['id']}/ui"
|
||||
)
|
||||
|
||||
if exists(path_ui):
|
||||
if {"template.html", "actions.py"}.issubset(listdir(path_ui)):
|
||||
db_plugin_page = (
|
||||
session.query(Plugin_pages)
|
||||
.with_entities(
|
||||
|
@ -1127,12 +1131,12 @@ class Database:
|
|||
|
||||
if db_plugin_page is None:
|
||||
with open(
|
||||
f"/usr/share/bunkerweb/core/{plugin['id']}/ui/template.html",
|
||||
f"{path_ui}/template.html",
|
||||
"r",
|
||||
) as file:
|
||||
template = file.read().encode("utf-8")
|
||||
with open(
|
||||
f"/usr/share/bunkerweb/core/{plugin['id']}/ui/actions.py",
|
||||
f"{path_ui}/actions.py",
|
||||
"r",
|
||||
) as file:
|
||||
actions = file.read().encode("utf-8")
|
||||
|
@ -1149,18 +1153,16 @@ class Database:
|
|||
else: # TODO test this
|
||||
updates = {}
|
||||
template_checksum = file_hash(
|
||||
f"/usr/share/bunkerweb/core/{plugin['id']}/ui/template.html"
|
||||
)
|
||||
actions_checksum = file_hash(
|
||||
f"/usr/share/bunkerweb/core/{plugin['id']}/ui/actions.py"
|
||||
f"{path_ui}/template.html"
|
||||
)
|
||||
actions_checksum = file_hash(f"{path_ui}/actions.py")
|
||||
|
||||
if (
|
||||
template_checksum
|
||||
!= db_plugin_page.template_checksum
|
||||
):
|
||||
with open(
|
||||
f"/usr/share/bunkerweb/core/{plugin['id']}/ui/template.html",
|
||||
f"{path_ui}/template.html",
|
||||
"r",
|
||||
) as file:
|
||||
updates.update(
|
||||
|
@ -1174,7 +1176,7 @@ class Database:
|
|||
|
||||
if actions_checksum != db_plugin_page.actions_checksum:
|
||||
with open(
|
||||
f"/usr/share/bunkerweb/core/{plugin['id']}/ui/actions.py",
|
||||
f"{path_ui}/actions.py",
|
||||
"r",
|
||||
) as file:
|
||||
updates.update(
|
||||
|
@ -1258,6 +1260,79 @@ class Database:
|
|||
|
||||
return ""
|
||||
|
||||
def get_plugins(self) -> List[Dict[str, Any]]:
|
||||
"""Get plugins."""
|
||||
plugins = []
|
||||
with self.__db_session() as session:
|
||||
for plugin in (
|
||||
session.query(Plugins)
|
||||
.with_entities(
|
||||
Plugins.id,
|
||||
Plugins.order,
|
||||
Plugins.name,
|
||||
Plugins.description,
|
||||
Plugins.version,
|
||||
Plugins.external,
|
||||
)
|
||||
.order_by(Plugins.order)
|
||||
.all()
|
||||
):
|
||||
page = (
|
||||
session.query(Plugin_pages)
|
||||
.with_entities(Plugin_pages.id)
|
||||
.filter_by(plugin_id=plugin.id)
|
||||
.first()
|
||||
)
|
||||
data = {
|
||||
"id": plugin.id,
|
||||
"order": plugin.order,
|
||||
"name": plugin.name,
|
||||
"description": plugin.description,
|
||||
"version": plugin.version,
|
||||
"external": plugin.external,
|
||||
"page": page is not None,
|
||||
"settings": {},
|
||||
}
|
||||
|
||||
for setting in (
|
||||
session.query(Settings)
|
||||
.with_entities(
|
||||
Settings.id,
|
||||
Settings.context,
|
||||
Settings.default,
|
||||
Settings.help,
|
||||
Settings.name,
|
||||
Settings.label,
|
||||
Settings.regex,
|
||||
Settings.type,
|
||||
Settings.multiple,
|
||||
)
|
||||
.filter_by(plugin_id=plugin.id)
|
||||
.all()
|
||||
):
|
||||
data["settings"][setting.id] = {
|
||||
"context": setting.context,
|
||||
"default": setting.default,
|
||||
"help": setting.help,
|
||||
"id": setting.name,
|
||||
"label": setting.label,
|
||||
"regex": setting.regex,
|
||||
"type": setting.type,
|
||||
} | ({"multiple": setting.multiple} if setting.multiple else {})
|
||||
|
||||
if setting.type == "select":
|
||||
data["settings"][setting.id]["select"] = [
|
||||
select.value
|
||||
for select in session.query(Selects)
|
||||
.with_entities(Selects.value)
|
||||
.filter_by(setting_id=setting.id)
|
||||
.all()
|
||||
]
|
||||
|
||||
plugins.append(data)
|
||||
|
||||
return plugins
|
||||
|
||||
def get_plugins_errors(self) -> int:
|
||||
"""Get plugins errors."""
|
||||
with self.__db_session() as session:
|
||||
|
@ -1381,3 +1456,33 @@ class Database:
|
|||
.all()
|
||||
)
|
||||
]
|
||||
|
||||
def get_plugin_actions(self, plugin: str) -> Optional[Any]:
|
||||
"""get actions file for the plugin"""
|
||||
with self.__db_session() as session:
|
||||
page = (
|
||||
session.query(Plugin_pages)
|
||||
.with_entities(Plugin_pages.actions_file)
|
||||
.filter_by(plugin_id=plugin)
|
||||
.first()
|
||||
)
|
||||
|
||||
if page is None:
|
||||
return None
|
||||
|
||||
return page.actions_file
|
||||
|
||||
def get_plugin_template(self, plugin: str) -> Optional[Any]:
|
||||
"""get template file for the plugin"""
|
||||
with self.__db_session() as session:
|
||||
page = (
|
||||
session.query(Plugin_pages)
|
||||
.with_entities(Plugin_pages.template_file)
|
||||
.filter_by(plugin_id=plugin)
|
||||
.first()
|
||||
)
|
||||
|
||||
if page is None:
|
||||
return None
|
||||
|
||||
return page.template_file
|
||||
|
|
|
@ -61,12 +61,10 @@ class Plugins(Base):
|
|||
external = Column(Boolean, default=False, nullable=False)
|
||||
|
||||
settings = relationship(
|
||||
"Settings", back_populates="plugin", cascade="all, delete, delete-orphan"
|
||||
"Settings", back_populates="plugin", cascade="all, delete-orphan"
|
||||
)
|
||||
jobs = relationship(
|
||||
"Jobs", back_populates="plugin", cascade="all, delete, delete-orphan"
|
||||
)
|
||||
pages = relationship("Plugin_pages", back_populates="plugin", cascade="all, delete")
|
||||
jobs = relationship("Jobs", back_populates="plugin", cascade="all, delete-orphan")
|
||||
pages = relationship("Plugin_pages", back_populates="plugin", cascade="all")
|
||||
|
||||
|
||||
class Settings(Base):
|
||||
|
@ -81,7 +79,7 @@ class Settings(Base):
|
|||
name = Column(String(256), primary_key=True)
|
||||
plugin_id = Column(
|
||||
String(64),
|
||||
ForeignKey("plugins.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("plugins.id", onupdate="cascade", ondelete="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
context = Column(CONTEXTS_ENUM, nullable=False)
|
||||
|
@ -92,12 +90,12 @@ class Settings(Base):
|
|||
type = Column(SETTINGS_TYPES_ENUM, nullable=False)
|
||||
multiple = Column(String(128), nullable=True)
|
||||
|
||||
selects = relationship("Selects", back_populates="setting", cascade="all, delete")
|
||||
selects = relationship("Selects", back_populates="setting", cascade="all")
|
||||
services = relationship(
|
||||
"Services_settings", back_populates="setting", cascade="all, delete"
|
||||
"Services_settings", back_populates="setting", cascade="all"
|
||||
)
|
||||
global_value = relationship(
|
||||
"Global_values", back_populates="setting", cascade="all, delete"
|
||||
"Global_values", back_populates="setting", cascade="all"
|
||||
)
|
||||
plugin = relationship("Plugins", back_populates="settings")
|
||||
|
||||
|
@ -107,7 +105,7 @@ class Global_values(Base):
|
|||
|
||||
setting_id = Column(
|
||||
String(256),
|
||||
ForeignKey("settings.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("settings.id", onupdate="cascade", ondelete="cascade"),
|
||||
primary_key=True,
|
||||
)
|
||||
value = Column(String(4096), nullable=False)
|
||||
|
@ -124,14 +122,12 @@ class Services(Base):
|
|||
method = Column(METHODS_ENUM, nullable=False)
|
||||
|
||||
settings = relationship(
|
||||
"Services_settings", back_populates="service", cascade="all, delete"
|
||||
"Services_settings", back_populates="service", cascade="all"
|
||||
)
|
||||
custom_configs = relationship(
|
||||
"Custom_configs", back_populates="service", cascade="all, delete"
|
||||
)
|
||||
jobs_cache = relationship(
|
||||
"Jobs_cache", back_populates="service", cascade="all, delete"
|
||||
"Custom_configs", back_populates="service", cascade="all"
|
||||
)
|
||||
jobs_cache = relationship("Jobs_cache", back_populates="service", cascade="all")
|
||||
|
||||
|
||||
class Services_settings(Base):
|
||||
|
@ -139,12 +135,12 @@ class Services_settings(Base):
|
|||
|
||||
service_id = Column(
|
||||
String(64),
|
||||
ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("services.id", onupdate="cascade", ondelete="cascade"),
|
||||
primary_key=True,
|
||||
)
|
||||
setting_id = Column(
|
||||
String(256),
|
||||
ForeignKey("settings.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("settings.id", onupdate="cascade", ondelete="cascade"),
|
||||
primary_key=True,
|
||||
)
|
||||
value = Column(String(4096), nullable=False)
|
||||
|
@ -162,7 +158,7 @@ class Jobs(Base):
|
|||
name = Column(String(128), primary_key=True)
|
||||
plugin_id = Column(
|
||||
String(64),
|
||||
ForeignKey("plugins.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("plugins.id", onupdate="cascade", ondelete="cascade"),
|
||||
)
|
||||
file_name = Column(String(256), nullable=False)
|
||||
every = Column(SCHEDULES_ENUM, nullable=False)
|
||||
|
@ -171,7 +167,7 @@ class Jobs(Base):
|
|||
last_run = Column(DateTime, nullable=True)
|
||||
|
||||
plugin = relationship("Plugins", back_populates="jobs")
|
||||
cache = relationship("Jobs_cache", back_populates="job", cascade="all, delete")
|
||||
cache = relationship("Jobs_cache", back_populates="job", cascade="all")
|
||||
|
||||
|
||||
class Plugin_pages(Base):
|
||||
|
@ -184,7 +180,7 @@ class Plugin_pages(Base):
|
|||
)
|
||||
plugin_id = Column(
|
||||
String(64),
|
||||
ForeignKey("plugins.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("plugins.id", onupdate="cascade", ondelete="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
template_file = Column(LargeBinary(length=(2**32) - 1), nullable=False)
|
||||
|
@ -206,12 +202,12 @@ class Jobs_cache(Base):
|
|||
)
|
||||
job_name = Column(
|
||||
String(128),
|
||||
ForeignKey("jobs.name", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("jobs.name", onupdate="cascade", ondelete="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
service_id = Column(
|
||||
String(64),
|
||||
ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("services.id", onupdate="cascade", ondelete="cascade"),
|
||||
nullable=True,
|
||||
)
|
||||
file_name = Column(
|
||||
|
@ -237,7 +233,7 @@ class Custom_configs(Base):
|
|||
)
|
||||
service_id = Column(
|
||||
String(64),
|
||||
ForeignKey("services.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("services.id", onupdate="cascade", ondelete="cascade"),
|
||||
nullable=True,
|
||||
)
|
||||
type = Column(CUSTOM_CONFIGS_TYPES_ENUM, nullable=False)
|
||||
|
@ -254,7 +250,7 @@ class Selects(Base):
|
|||
|
||||
setting_id = Column(
|
||||
String(256),
|
||||
ForeignKey("settings.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||
ForeignKey("settings.id", onupdate="cascade", ondelete="cascade"),
|
||||
primary_key=True,
|
||||
)
|
||||
value = Column(String(256), primary_key=True)
|
||||
|
|
|
@ -14,9 +14,11 @@ class Configurator:
|
|||
self,
|
||||
settings: str,
|
||||
core: Union[str, dict],
|
||||
plugins: str,
|
||||
plugins: Union[str, dict],
|
||||
variables: Union[str, dict],
|
||||
logger: Logger,
|
||||
*,
|
||||
plugins_settings: list = None,
|
||||
):
|
||||
self.__logger = logger
|
||||
self.__settings = self.__load_settings(settings)
|
||||
|
@ -26,8 +28,12 @@ class Configurator:
|
|||
else:
|
||||
self.__core = core
|
||||
|
||||
self.__plugins_settings = []
|
||||
self.__plugins = self.__load_plugins(plugins, "plugins")
|
||||
self.__plugins_settings = plugins_settings or []
|
||||
|
||||
if isinstance(plugins, str):
|
||||
self.__plugins = self.__load_plugins(plugins, "plugins")
|
||||
else:
|
||||
self.__plugins = plugins
|
||||
|
||||
if isinstance(variables, str):
|
||||
self.__variables = self.__load_variables(variables)
|
||||
|
|
|
@ -145,6 +145,18 @@ if __name__ == "__main__":
|
|||
db = None
|
||||
apis = []
|
||||
|
||||
plugins = args.plugins
|
||||
plugins_settings = None
|
||||
if not exists("/usr/sbin/nginx") and args.method == "ui":
|
||||
db = Database(logger)
|
||||
plugins = {}
|
||||
plugins_settings = []
|
||||
for plugin in db.get_plugins():
|
||||
del plugin["page"]
|
||||
del plugin["external"]
|
||||
plugins_settings.append(plugin)
|
||||
plugins.update(plugin["settings"])
|
||||
|
||||
# Check existences and permissions
|
||||
logger.info("Checking arguments ...")
|
||||
files = [args.settings] + ([args.variables] if args.variables else [])
|
||||
|
@ -200,7 +212,12 @@ if __name__ == "__main__":
|
|||
# Compute the config
|
||||
logger.info("Computing config ...")
|
||||
config = Configurator(
|
||||
args.settings, core_settings, args.plugins, args.variables, logger
|
||||
args.settings,
|
||||
core_settings,
|
||||
plugins,
|
||||
args.variables,
|
||||
logger,
|
||||
plugins_settings=plugins_settings,
|
||||
)
|
||||
config_files = config.get_config()
|
||||
custom_confs = [
|
||||
|
@ -288,7 +305,12 @@ if __name__ == "__main__":
|
|||
if config_files is None:
|
||||
logger.info("Computing config ...")
|
||||
config = Configurator(
|
||||
args.settings, core_settings, args.plugins, tmp_config, logger
|
||||
args.settings,
|
||||
core_settings,
|
||||
plugins,
|
||||
tmp_config,
|
||||
logger,
|
||||
plugins_settings=plugins_settings,
|
||||
)
|
||||
config_files = config.get_config()
|
||||
|
||||
|
|
293
src/ui/main.py
293
src/ui/main.py
|
@ -1,7 +1,9 @@
|
|||
from contextlib import suppress
|
||||
from importlib.machinery import SourceFileLoader, SourcelessFileLoader
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from signal import SIGINT, signal, SIGTERM
|
||||
from tempfile import NamedTemporaryFile
|
||||
from bs4 import BeautifulSoup
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
@ -688,25 +690,40 @@ def plugins():
|
|||
variables = deepcopy(request.form.to_dict())
|
||||
del variables["csrf_token"]
|
||||
|
||||
if variables["external"] == "false":
|
||||
if variables["external"] != "True":
|
||||
flash(f"Can't delete internal plugin {variables['name']}", "error")
|
||||
return redirect(url_for("loading", next=url_for("plugins"))), 500
|
||||
|
||||
variables["path"] = f"/etc/bunkerweb/plugins/{variables['name']}"
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
plugins = app.config["CONFIG"].get_plugins()
|
||||
for plugin in deepcopy(plugins):
|
||||
if plugin["external"] is False or plugin["id"] == variables["name"]:
|
||||
del plugins[plugins.index(plugin)]
|
||||
|
||||
operation = app.config["CONFIGFILES"].check_path(
|
||||
variables["path"], "/etc/bunkerweb/plugins/"
|
||||
)
|
||||
err = db.update_external_plugins(plugins)
|
||||
if err:
|
||||
flash(
|
||||
f"Couldn't update external plugins to database: {err}",
|
||||
"error",
|
||||
)
|
||||
else:
|
||||
variables["path"] = f"/etc/bunkerweb/plugins/{variables['name']}"
|
||||
|
||||
if operation:
|
||||
flash(operation, "error")
|
||||
return redirect(url_for("loading", next=url_for("plugins"))), 500
|
||||
operation = app.config["CONFIGFILES"].check_path(
|
||||
variables["path"], "/etc/bunkerweb/plugins/"
|
||||
)
|
||||
|
||||
operation, error = app.config["CONFIGFILES"].delete_path(variables["path"])
|
||||
if operation:
|
||||
flash(operation, "error")
|
||||
return redirect(url_for("loading", next=url_for("plugins"))), 500
|
||||
|
||||
if error:
|
||||
flash(operation, "error")
|
||||
return redirect(url_for("loading", next=url_for("plugins")))
|
||||
operation, error = app.config["CONFIGFILES"].delete_path(
|
||||
variables["path"]
|
||||
)
|
||||
|
||||
if error:
|
||||
flash(operation, "error")
|
||||
return redirect(url_for("loading", next=url_for("plugins")))
|
||||
else:
|
||||
if not exists("/var/tmp/bunkerweb/ui") or not listdir(
|
||||
"/var/tmp/bunkerweb/ui"
|
||||
|
@ -758,13 +775,33 @@ def plugins():
|
|||
)
|
||||
raise Exception
|
||||
|
||||
if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
|
||||
raise FileExistsError
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
plugins = app.config["CONFIG"].get_plugins()
|
||||
for plugin in deepcopy(plugins):
|
||||
if plugin["id"] == folder_name:
|
||||
raise FileExistsError
|
||||
elif plugin["external"] is False:
|
||||
del plugins[plugins.index(plugin)]
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
plugins.append(plugin_file)
|
||||
err = db.update_external_plugins(plugins)
|
||||
if err:
|
||||
error = 1
|
||||
flash(
|
||||
f"Couldn't update external plugins to database: {err}",
|
||||
"error",
|
||||
)
|
||||
raise Exception
|
||||
else:
|
||||
if exists(
|
||||
f"/etc/bunkerweb/plugins/{folder_name}"
|
||||
):
|
||||
raise FileExistsError
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
except KeyError:
|
||||
zip_file.extractall(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}"
|
||||
|
@ -812,13 +849,33 @@ def plugins():
|
|||
)
|
||||
raise Exception
|
||||
|
||||
if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
|
||||
raise FileExistsError
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
plugins = app.config["CONFIG"].get_plugins()
|
||||
for plugin in deepcopy(plugins):
|
||||
if plugin["id"] == folder_name:
|
||||
raise FileExistsError
|
||||
elif plugin["external"] is False:
|
||||
del plugins[plugins.index(plugin)]
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
plugins.append(plugin_file)
|
||||
err = db.update_external_plugins(plugins)
|
||||
if err:
|
||||
error = 1
|
||||
flash(
|
||||
f"Couldn't update external plugins to database: {err}",
|
||||
"error",
|
||||
)
|
||||
raise Exception
|
||||
else:
|
||||
if exists(
|
||||
f"/etc/bunkerweb/plugins/{folder_name}"
|
||||
):
|
||||
raise FileExistsError
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
except BadZipFile:
|
||||
errors += 1
|
||||
error = 1
|
||||
|
@ -861,13 +918,33 @@ def plugins():
|
|||
)
|
||||
raise Exception
|
||||
|
||||
if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
|
||||
raise FileExistsError
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
plugins = app.config["CONFIG"].get_plugins()
|
||||
for plugin in deepcopy(plugins):
|
||||
if plugin["id"] == folder_name:
|
||||
raise FileExistsError
|
||||
elif plugin["external"] is False:
|
||||
del plugins[plugins.index(plugin)]
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
plugins.append(plugin_file)
|
||||
err = db.update_external_plugins(plugins)
|
||||
if err:
|
||||
error = 1
|
||||
flash(
|
||||
f"Couldn't update external plugins to database: {err}",
|
||||
"error",
|
||||
)
|
||||
raise Exception
|
||||
else:
|
||||
if exists(
|
||||
f"/etc/bunkerweb/plugins/{folder_name}"
|
||||
):
|
||||
raise FileExistsError
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
except KeyError:
|
||||
tar_file.extractall(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}",
|
||||
|
@ -915,13 +992,33 @@ def plugins():
|
|||
)
|
||||
raise Exception
|
||||
|
||||
if exists(f"/etc/bunkerweb/plugins/{folder_name}"):
|
||||
raise FileExistsError
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
plugins = app.config["CONFIG"].get_plugins()
|
||||
for plugin in deepcopy(plugins):
|
||||
if plugin["id"] == folder_name:
|
||||
raise FileExistsError
|
||||
elif plugin["external"] is False:
|
||||
del plugins[plugins.index(plugin)]
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
plugins.append(plugin_file)
|
||||
err = db.update_external_plugins(plugins)
|
||||
if err:
|
||||
error = 1
|
||||
flash(
|
||||
f"Couldn't update external plugins to database: {err}",
|
||||
"error",
|
||||
)
|
||||
raise Exception
|
||||
else:
|
||||
if exists(
|
||||
f"/etc/bunkerweb/plugins/{folder_name}"
|
||||
):
|
||||
raise FileExistsError
|
||||
|
||||
copytree(
|
||||
f"/var/tmp/bunkerweb/ui/{temp_folder_name}/{dirs[0]}",
|
||||
f"/etc/bunkerweb/plugins/{folder_name}",
|
||||
)
|
||||
except ReadError:
|
||||
errors += 1
|
||||
error = 1
|
||||
|
@ -993,14 +1090,12 @@ def plugins():
|
|||
# Fix permissions for plugins folders
|
||||
for root, dirs, files in walk("/etc/bunkerweb/plugins", topdown=False):
|
||||
for name in files + dirs:
|
||||
chown(join(root, name), 101, 101)
|
||||
chown(join(root, name), "root", 101)
|
||||
chmod(join(root, name), 0o770)
|
||||
|
||||
if operation:
|
||||
flash(operation)
|
||||
|
||||
app.config["CONFIG"].reload_plugins()
|
||||
|
||||
# Reload instances
|
||||
app.config["RELOADING"] = True
|
||||
Thread(
|
||||
|
@ -1023,19 +1118,28 @@ def plugins():
|
|||
|
||||
if request.args.get("plugin_id", False):
|
||||
plugin_id = request.args.get("plugin_id")
|
||||
page_path = ""
|
||||
template = None
|
||||
|
||||
if exists(f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"):
|
||||
page_path = f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"
|
||||
elif exists(f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"):
|
||||
page_path = f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
page = db.get_plugin_template(plugin_id)
|
||||
|
||||
if page is not None:
|
||||
template = Template(page.decode("utf-8"))
|
||||
else:
|
||||
flash(f"Plugin {plugin_id} not found", "error")
|
||||
page_path = ""
|
||||
|
||||
if page_path:
|
||||
with open(page_path, "r") as f:
|
||||
template = Template(f.read())
|
||||
if exists(f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"):
|
||||
page_path = f"/etc/bunkerweb/plugins/{plugin_id}/ui/template.html"
|
||||
elif exists(f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"):
|
||||
page_path = f"/usr/share/bunkerweb/core/{plugin_id}/ui/template.html"
|
||||
else:
|
||||
flash(f"Plugin {plugin_id} not found", "error")
|
||||
|
||||
if page_path:
|
||||
with open(page_path, "r") as f:
|
||||
template = Template(f.read())
|
||||
|
||||
if template is not None:
|
||||
return template.render(
|
||||
csrf_token=generate_csrf,
|
||||
url_for=url_for,
|
||||
|
@ -1047,7 +1151,6 @@ def plugins():
|
|||
),
|
||||
)
|
||||
|
||||
app.config["CONFIG"].reload_plugins()
|
||||
plugins = app.config["CONFIG"].get_plugins()
|
||||
plugins_internal = 0
|
||||
plugins_external = 0
|
||||
|
@ -1096,33 +1199,66 @@ def custom_plugin(plugin):
|
|||
)
|
||||
return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin)))
|
||||
|
||||
if not exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py") and not exists(
|
||||
f"/usr/share/bunkerweb/core/{plugin}/ui/actions.py"
|
||||
):
|
||||
flash(
|
||||
f"The <i>actions.py</i> file for the plugin <b>{plugin}</b> does not exist",
|
||||
"error",
|
||||
)
|
||||
return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin)))
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
module = db.get_plugin_actions(plugin)
|
||||
|
||||
# Add the custom plugin to sys.path
|
||||
sys_path.append(
|
||||
(
|
||||
"/etc/bunkerweb/plugins"
|
||||
if exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py")
|
||||
else "/usr/share/bunkerweb/core"
|
||||
if module is None:
|
||||
flash(
|
||||
f"The <i>actions.py</i> file for the plugin <b>{plugin}</b> does not exist",
|
||||
"error",
|
||||
)
|
||||
return redirect(
|
||||
url_for("loading", next=url_for("plugins", plugin_id=plugin))
|
||||
)
|
||||
|
||||
try:
|
||||
# Try to import the custom plugin
|
||||
with NamedTemporaryFile(mode="wb", suffix=".py", delete=True) as temp:
|
||||
temp.write(module)
|
||||
temp.flush()
|
||||
temp.seek(0)
|
||||
loader = SourceFileLoader("actions", temp.name)
|
||||
actions = loader.load_module()
|
||||
except:
|
||||
flash(
|
||||
f"An error occurred while importing the plugin <b>{plugin}</b>:<br/>{format_exc()}",
|
||||
"error",
|
||||
)
|
||||
return redirect(
|
||||
url_for("loading", next=url_for("plugins", plugin_id=plugin))
|
||||
)
|
||||
else:
|
||||
if not exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py") and not exists(
|
||||
f"/usr/share/bunkerweb/core/{plugin}/ui/actions.py"
|
||||
):
|
||||
flash(
|
||||
f"The <i>actions.py</i> file for the plugin <b>{plugin}</b> does not exist",
|
||||
"error",
|
||||
)
|
||||
return redirect(
|
||||
url_for("loading", next=url_for("plugins", plugin_id=plugin))
|
||||
)
|
||||
|
||||
# Add the custom plugin to sys.path
|
||||
sys_path.append(
|
||||
(
|
||||
"/etc/bunkerweb/plugins"
|
||||
if exists(f"/etc/bunkerweb/plugins/{plugin}/ui/actions.py")
|
||||
else "/usr/share/bunkerweb/core"
|
||||
)
|
||||
+ f"/{plugin}/ui/"
|
||||
)
|
||||
+ f"/{plugin}/ui/"
|
||||
)
|
||||
try:
|
||||
# Try to import the custom plugin
|
||||
import actions
|
||||
except:
|
||||
flash(
|
||||
f"An error occurred while importing the plugin <b>{plugin}</b>:<br/>{format_exc()}",
|
||||
"error",
|
||||
)
|
||||
return redirect(url_for("loading", next=url_for("plugins", plugin_id=plugin)))
|
||||
try:
|
||||
# Try to import the custom plugin
|
||||
import actions
|
||||
except:
|
||||
flash(
|
||||
f"An error occurred while importing the plugin <b>{plugin}</b>:<br/>{format_exc()}",
|
||||
"error",
|
||||
)
|
||||
return redirect(
|
||||
url_for("loading", next=url_for("plugins", plugin_id=plugin))
|
||||
)
|
||||
|
||||
error = False
|
||||
res = None
|
||||
|
@ -1145,10 +1281,11 @@ def custom_plugin(plugin):
|
|||
)
|
||||
error = True
|
||||
finally:
|
||||
# Remove the custom plugin from the shared library
|
||||
sys_path.pop()
|
||||
sys_modules.pop("actions")
|
||||
del actions
|
||||
if exists("/usr/sbin/nginx"):
|
||||
# Remove the custom plugin from the shared library
|
||||
sys_path.pop()
|
||||
sys_modules.pop("actions")
|
||||
del actions
|
||||
|
||||
if (
|
||||
request.method != "POST"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from copy import deepcopy
|
||||
from os import listdir, remove
|
||||
from os import listdir, mkdir, remove
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from time import sleep
|
||||
from flask import flash
|
||||
from os.path import exists, isfile
|
||||
|
@ -35,54 +36,6 @@ class Config:
|
|||
sleep(3)
|
||||
env = self.__db.get_config()
|
||||
|
||||
self.reload_plugins()
|
||||
|
||||
def reload_plugins(self) -> None:
|
||||
self.__plugins = []
|
||||
external_plugins = []
|
||||
|
||||
for foldername in list(iglob("/etc/bunkerweb/plugins/*")) + list(
|
||||
iglob("/usr/share/bunkerweb/core/*")
|
||||
):
|
||||
content = listdir(foldername)
|
||||
if "plugin.json" not in content:
|
||||
continue
|
||||
|
||||
with open(f"{foldername}/plugin.json", "r") as f:
|
||||
plugin = json_load(f)
|
||||
|
||||
plugin.update(
|
||||
{
|
||||
"page": False,
|
||||
"external": foldername.startswith("/etc/bunkerweb/plugins"),
|
||||
}
|
||||
)
|
||||
|
||||
if plugin["external"] is True:
|
||||
external_plugin = deepcopy(plugin)
|
||||
del external_plugin["external"]
|
||||
del external_plugin["page"]
|
||||
external_plugins.append(external_plugin)
|
||||
|
||||
if "ui" in content:
|
||||
if "template.html" in listdir(f"{foldername}/ui"):
|
||||
plugin["page"] = True
|
||||
|
||||
self.__plugins.append(plugin)
|
||||
|
||||
self.__plugins.sort(key=lambda plugin: plugin.get("name"))
|
||||
self.__plugins_settings = {
|
||||
**{k: v for x in self.__plugins for k, v in x["settings"].items()},
|
||||
**self.__settings,
|
||||
}
|
||||
|
||||
if external_plugins:
|
||||
err = self.__db.update_external_plugins(external_plugins)
|
||||
if err:
|
||||
self.__logger.error(
|
||||
f"Couldn't update external plugins to database: {err}",
|
||||
)
|
||||
|
||||
def __env_to_dict(self, filename: str) -> dict:
|
||||
"""Converts the content of an env file into a dict
|
||||
|
||||
|
@ -139,17 +92,17 @@ class Config:
|
|||
conf = deepcopy(global_conf)
|
||||
|
||||
servers = []
|
||||
plugins_settings = self.get_plugins_settings()
|
||||
for service in services_conf:
|
||||
server_name = service["SERVER_NAME"].split(" ")[0]
|
||||
for k in service.keys():
|
||||
key_without_server_name = k.replace(f"{server_name}_", "")
|
||||
if (
|
||||
self.__plugins_settings[key_without_server_name]["context"]
|
||||
!= "global"
|
||||
if key_without_server_name in self.__plugins_settings
|
||||
plugins_settings[key_without_server_name]["context"] != "global"
|
||||
if key_without_server_name in plugins_settings
|
||||
else True
|
||||
):
|
||||
if not k.startswith(server_name) or k in self.__plugins_settings:
|
||||
if not k.startswith(server_name) or k in plugins_settings:
|
||||
conf[f"{server_name}_{k}"] = service[k]
|
||||
else:
|
||||
conf[k] = service[k]
|
||||
|
@ -178,10 +131,44 @@ class Config:
|
|||
remove(env_file)
|
||||
|
||||
def get_plugins_settings(self) -> dict:
|
||||
return self.__plugins_settings
|
||||
return {
|
||||
**{k: v for x in self.get_plugins() for k, v in x["settings"].items()},
|
||||
**self.__settings,
|
||||
}
|
||||
|
||||
def get_plugins(self) -> List[dict]:
|
||||
return self.__plugins
|
||||
if not exists("/usr/sbin/nginx"):
|
||||
plugins = self.__db.get_plugins()
|
||||
plugins.sort(key=lambda x: x["name"])
|
||||
return plugins
|
||||
|
||||
plugins = []
|
||||
|
||||
for foldername in list(iglob("/etc/bunkerweb/plugins/*")) + list(
|
||||
iglob("/usr/share/bunkerweb/core/*")
|
||||
):
|
||||
content = listdir(foldername)
|
||||
if "plugin.json" not in content:
|
||||
continue
|
||||
|
||||
with open(f"{foldername}/plugin.json", "r") as f:
|
||||
plugin = json_load(f)
|
||||
|
||||
plugin.update(
|
||||
{
|
||||
"page": False,
|
||||
"external": foldername.startswith("/etc/bunkerweb/plugins"),
|
||||
}
|
||||
)
|
||||
|
||||
if "ui" in content:
|
||||
if "template.html" in listdir(f"{foldername}/ui"):
|
||||
plugin["page"] = True
|
||||
|
||||
plugins.append(plugin)
|
||||
|
||||
plugins.sort(key=lambda x: x["name"])
|
||||
return plugins
|
||||
|
||||
def get_settings(self) -> dict:
|
||||
return self.__settings
|
||||
|
@ -212,6 +199,7 @@ class Config:
|
|||
"""
|
||||
if exists("/usr/sbin/nginx"):
|
||||
services = []
|
||||
plugins_settings = self.get_plugins_settings()
|
||||
for filename in iglob("/etc/nginx/**/variables.env"):
|
||||
service = filename.split("/")[3]
|
||||
env = {
|
||||
|
@ -219,8 +207,7 @@ class Config:
|
|||
{"value": v, "method": "ui"} if methods is True else v
|
||||
)
|
||||
for k, v in self.__env_to_dict(filename).items()
|
||||
if k.startswith(f"{service}_")
|
||||
or k in self.__plugins_settings.keys()
|
||||
if k.startswith(f"{service}_") or k in plugins_settings.keys()
|
||||
}
|
||||
services.append(env)
|
||||
|
||||
|
@ -242,11 +229,12 @@ class Config:
|
|||
Return the error code
|
||||
"""
|
||||
error = 0
|
||||
plugins_settings = self.get_plugins_settings()
|
||||
for k, v in variables.items():
|
||||
check = False
|
||||
|
||||
if k in self.__plugins_settings:
|
||||
if _global ^ (self.__plugins_settings[k]["context"] == "global"):
|
||||
if k in plugins_settings:
|
||||
if _global ^ (plugins_settings[k]["context"] == "global"):
|
||||
error = 1
|
||||
flash(f"Variable {k} is not valid.", "error")
|
||||
continue
|
||||
|
@ -255,16 +243,16 @@ class Config:
|
|||
else:
|
||||
setting = k[0 : k.rfind("_")]
|
||||
if (
|
||||
setting not in self.__plugins_settings
|
||||
or "multiple" not in self.__plugins_settings[setting]
|
||||
setting not in plugins_settings
|
||||
or "multiple" not in plugins_settings[setting]
|
||||
):
|
||||
error = 1
|
||||
flash(f"Variable {k} is not valid.", "error")
|
||||
continue
|
||||
|
||||
if not (
|
||||
_global ^ (self.__plugins_settings[setting]["context"] == "global")
|
||||
) and re_search(self.__plugins_settings[setting]["regex"], v):
|
||||
_global ^ (plugins_settings[setting]["context"] == "global")
|
||||
) and re_search(plugins_settings[setting]["regex"], v):
|
||||
check = True
|
||||
|
||||
if not check:
|
||||
|
|
Loading…
Reference in New Issue