mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Add draft back end logic for services
This commit is contained in:
parent
fa014cfef9
commit
b0e5eacbbf
5 changed files with 97 additions and 85 deletions
|
|
@ -621,23 +621,42 @@ class Database:
|
|||
|
||||
if config:
|
||||
config.pop("DATABASE_URI", None)
|
||||
db_services = session.query(Services).with_entities(Services.id, Services.method).all()
|
||||
db_ids = [service.id for service in db_services]
|
||||
db_services = session.query(Services).with_entities(Services.id, Services.method, Services.is_draft).all()
|
||||
db_ids: Dict[str, dict] = {service.id: {"method": service.method, "is_draft": service.is_draft} for service in db_services}
|
||||
missing_ids = []
|
||||
services = config.get("SERVER_NAME", [])
|
||||
|
||||
if isinstance(services, str):
|
||||
services = services.split(" ")
|
||||
|
||||
if db_services:
|
||||
missing_ids = [service.id for service in db_services if (service.method == method) and service.id not in services]
|
||||
missing_ids = [service.id for service in db_services if service.method == method and service.id not in services]
|
||||
|
||||
if missing_ids:
|
||||
# Remove services that are no longer in the list
|
||||
session.query(Services).filter(Services.id.in_(missing_ids)).delete()
|
||||
|
||||
drafts = {service for service in services if config.pop(f"{service}_IS_DRAFT", "no") == "yes"}
|
||||
db_drafts = {service.id for service in db_services if service.is_draft}
|
||||
|
||||
if db_drafts:
|
||||
missing_drafts = [service.id for service in db_services if service.method == method and service.id not in drafts and service.id not in missing_ids]
|
||||
|
||||
if missing_drafts:
|
||||
# Remove drafts that are no longer in the list
|
||||
session.query(Services).filter(Services.id.in_(missing_drafts)).update({Services.is_draft: False})
|
||||
|
||||
for draft in drafts:
|
||||
if draft not in db_drafts:
|
||||
if draft not in db_ids:
|
||||
to_put.append(Services(id=draft, method=method, is_draft=True))
|
||||
db_ids[draft] = {"method": method, "is_draft": True}
|
||||
elif method == db_ids[draft]["method"]:
|
||||
session.query(Services).filter(Services.id == draft).update({Services.is_draft: True})
|
||||
|
||||
if config.get("MULTISITE", "no") == "yes":
|
||||
global_values = []
|
||||
for key, value in deepcopy(config).items():
|
||||
for key, value in config.copy().items():
|
||||
suffix = 0
|
||||
original_key = deepcopy(key)
|
||||
if self.suffix_rx.search(key):
|
||||
|
|
@ -653,8 +672,8 @@ class Database:
|
|||
continue
|
||||
|
||||
if server_name not in db_ids:
|
||||
to_put.append(Services(id=server_name, method=method))
|
||||
db_ids.append(server_name)
|
||||
to_put.append(Services(id=server_name, method=method, is_draft=server_name in drafts))
|
||||
db_ids[server_name] = {"method": method, "is_draft": server_name in drafts}
|
||||
|
||||
key = key.replace(f"{server_name}_", "")
|
||||
setting = session.query(Settings).with_entities(Settings.default).filter_by(id=key).first()
|
||||
|
|
@ -886,7 +905,7 @@ class Database:
|
|||
|
||||
return message
|
||||
|
||||
def get_config(self, methods: bool = False) -> Dict[str, Any]:
|
||||
def get_config(self, methods: bool = False, with_drafts: bool = False) -> Dict[str, Any]:
|
||||
"""Get the config from the database"""
|
||||
with self.__db_session() as session:
|
||||
config = {}
|
||||
|
|
@ -922,8 +941,14 @@ class Database:
|
|||
|
||||
is_multisite = config.get("MULTISITE", {"value": "no"})["value"] == "yes" if methods else config.get("MULTISITE", "no") == "yes"
|
||||
|
||||
if with_drafts:
|
||||
services = session.query(Services).with_entities(Services.id, Services.is_draft).all()
|
||||
else:
|
||||
services = session.query(Services).with_entities(Services.id, Services.is_draft).filter_by(is_draft=False).all()
|
||||
|
||||
if is_multisite:
|
||||
for service in session.query(Services).with_entities(Services.id).all():
|
||||
for service in services:
|
||||
config[f"{service.id}_IS_DRAFT"] = "yes" if service.is_draft else "no"
|
||||
checked_settings = []
|
||||
for key, value in deepcopy(config).items():
|
||||
original_key = key
|
||||
|
|
@ -962,7 +987,7 @@ class Database:
|
|||
}
|
||||
)
|
||||
|
||||
servers = " ".join(service.id for service in session.query(Services).all())
|
||||
servers = " ".join(service.id for service in services)
|
||||
config["SERVER_NAME"] = servers if not methods else {"value": servers, "global": True, "method": "default"}
|
||||
|
||||
return config
|
||||
|
|
@ -991,35 +1016,26 @@ class Database:
|
|||
)
|
||||
]
|
||||
|
||||
def get_services_settings(self, methods: bool = False) -> List[Dict[str, Any]]:
|
||||
def get_services_settings(self, methods: bool = False, with_drafts: bool = False) -> List[Dict[str, Any]]:
|
||||
"""Get the services' configs from the database"""
|
||||
services = []
|
||||
config = self.get_config(methods=methods)
|
||||
with self.__db_session() as session:
|
||||
service_names = [service.id for service in session.query(Services).with_entities(Services.id).all()]
|
||||
for service in service_names:
|
||||
service_settings = []
|
||||
tmp_config = deepcopy(config)
|
||||
config = self.get_config(methods=methods, with_drafts=with_drafts)
|
||||
service_names = config["SERVER_NAME"]["value"].split(" ") if methods else config["SERVER_NAME"].split(" ")
|
||||
for service in service_names:
|
||||
service_settings = []
|
||||
tmp_config = deepcopy(config)
|
||||
|
||||
for key, value in deepcopy(tmp_config).items():
|
||||
if key.startswith(f"{service}_"):
|
||||
setting = key.replace(f"{service}_", "")
|
||||
service_settings.append(setting)
|
||||
tmp_config[setting] = tmp_config.pop(key)
|
||||
elif any(key.startswith(f"{s}_") for s in service_names):
|
||||
tmp_config.pop(key)
|
||||
elif key not in service_settings:
|
||||
tmp_config[key] = (
|
||||
{
|
||||
"value": value["value"],
|
||||
"global": value["global"],
|
||||
"method": value["method"],
|
||||
}
|
||||
if methods
|
||||
else value
|
||||
)
|
||||
for key, value in deepcopy(tmp_config).items():
|
||||
if key.startswith(f"{service}_"):
|
||||
setting = key.replace(f"{service}_", "")
|
||||
service_settings.append(setting)
|
||||
tmp_config[setting] = tmp_config.pop(key)
|
||||
elif any(key.startswith(f"{s}_") for s in service_names):
|
||||
tmp_config.pop(key)
|
||||
elif key not in service_settings:
|
||||
tmp_config[key] = {"value": value["value"], "global": value["global"], "method": value["method"]} if methods else value
|
||||
|
||||
services.append(tmp_config)
|
||||
services.append(tmp_config)
|
||||
|
||||
return services
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ class Services(Base):
|
|||
|
||||
id = Column(String(64), primary_key=True)
|
||||
method = Column(METHODS_ENUM, nullable=False)
|
||||
is_draft = Column(Boolean, default=False, nullable=False)
|
||||
|
||||
settings = relationship("Services_settings", back_populates="service", cascade="all")
|
||||
custom_configs = relationship("Custom_configs", back_populates="service", cascade="all")
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
"context": "global",
|
||||
"default": "no",
|
||||
"help": "Internal use : set to yes when BW is loading.",
|
||||
"id": "internal-use",
|
||||
"label": "internal use",
|
||||
"id": "internal-use-loading",
|
||||
"label": "internal use loading",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
|
|
@ -288,5 +288,14 @@
|
|||
"label": "Use IPv6",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"IS_DRAFT": {
|
||||
"context": "multisite",
|
||||
"default": "no",
|
||||
"help": "Internal use : set to yes when the service is in draft mode.",
|
||||
"id": "internal-use-draft",
|
||||
"label": "internal use draft",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ LOG_RX = re_compile(r"^(?P<date>\d+/\d+/\d+\s\d+:\d+:\d+)\s\[(?P<level>[a-z]+)\]
|
|||
REVERSE_PROXY_PATH = re_compile(r"^(?P<host>https?://.{1,255}(:((6553[0-5])|(655[0-2]\d)|(65[0-4]\d{2})|(6[0-4]\d{3})|([1-5]\d{4})|([0-5]{0,5})|(\d{1,4})))?)$")
|
||||
|
||||
|
||||
def manage_bunkerweb(method: str, *args, operation: str = "reloads"):
|
||||
def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: bool = False):
|
||||
# Do the operation
|
||||
error = False
|
||||
if method == "services":
|
||||
|
|
@ -224,27 +224,27 @@ def manage_bunkerweb(method: str, *args, operation: str = "reloads"):
|
|||
deleted = False
|
||||
|
||||
if operation == "new":
|
||||
operation, error = app.config["CONFIG"].new_service(args[0])
|
||||
operation, error = app.config["CONFIG"].new_service(args[0], is_draft=is_draft)
|
||||
elif operation == "edit":
|
||||
if args[1].split(" ")[0] != args[2].split(" ")[0] and service_custom_confs:
|
||||
for service_custom_conf in service_custom_confs:
|
||||
if listdir(service_custom_conf):
|
||||
move(service_custom_conf, service_custom_conf.replace(f"{sep}{args[1].split(' ')[0]}", f"{sep}{args[2].split(' ')[0]}"))
|
||||
moved = True
|
||||
operation, error = app.config["CONFIG"].edit_service(args[1], args[0], check_changes=not moved)
|
||||
operation, error = app.config["CONFIG"].edit_service(args[1], args[0], check_changes=not is_draft and not moved, is_draft=is_draft)
|
||||
elif operation == "delete":
|
||||
for service_custom_conf in glob(join(sep, "etc", "bunkerweb", "configs", "*", args[2].split(" ")[0])):
|
||||
if listdir(service_custom_conf):
|
||||
rmtree(service_custom_conf, ignore_errors=True)
|
||||
deleted = True
|
||||
operation, error = app.config["CONFIG"].delete_service(args[2], check_changes=not deleted)
|
||||
operation, error = app.config["CONFIG"].delete_service(args[2], check_changes=not is_draft and not deleted)
|
||||
|
||||
if error:
|
||||
app.config["TO_FLASH"].append({"content": operation, "type": "error"})
|
||||
else:
|
||||
app.config["TO_FLASH"].append({"content": operation, "type": "success"})
|
||||
|
||||
if moved or deleted:
|
||||
if not is_draft and (moved or deleted):
|
||||
changes = ["config", "custom_configs"]
|
||||
error = app.config["CONFIGFILES"].save_configs(check_changes=False)
|
||||
if error:
|
||||
|
|
@ -693,17 +693,17 @@ def instances():
|
|||
def services():
|
||||
if request.method == "POST":
|
||||
# Check operation
|
||||
if "operation" not in request.form or request.form["operation"] not in (
|
||||
"new",
|
||||
"edit",
|
||||
"delete",
|
||||
):
|
||||
if "operation" not in request.form or request.form["operation"] not in ("new", "edit", "delete"):
|
||||
flash("Missing operation parameter on /services.", "error")
|
||||
return redirect(url_for("loading", next=url_for("services")))
|
||||
elif "is_draft" not in request.form or request.form["is_draft"] not in ("yes", "no"):
|
||||
flash("Missing is_draft parameter on /services.", "error")
|
||||
return redirect(url_for("loading", next=url_for("services")))
|
||||
|
||||
# Check variables
|
||||
variables = deepcopy(request.form.to_dict())
|
||||
del variables["csrf_token"]
|
||||
is_draft = variables.pop("is_draft") == "yes"
|
||||
|
||||
if "OLD_SERVER_NAME" not in request.form and request.form["operation"] == "edit":
|
||||
flash("Missing OLD_SERVER_NAME parameter.", "error")
|
||||
|
|
@ -717,7 +717,7 @@ def services():
|
|||
del variables["OLD_SERVER_NAME"]
|
||||
|
||||
# Edit check fields and remove already existing ones
|
||||
config = app.config["CONFIG"].get_config(methods=False)
|
||||
config = app.config["CONFIG"].get_config(methods=False, with_drafts=True)
|
||||
server_name = variables["SERVER_NAME"].split(" ")[0]
|
||||
for variable, value in deepcopy(variables).items():
|
||||
if variable.endswith("SCHEMA"):
|
||||
|
|
@ -732,11 +732,8 @@ def services():
|
|||
if variable in variables and variable != "SERVER_NAME" and value == config.get(f"{server_name}_{variable}" if request.form["operation"] == "edit" else variable, None):
|
||||
del variables[variable]
|
||||
|
||||
if request.form["operation"] == "edit" and len(variables) == 1 and "SERVER_NAME" in variables and variables["SERVER_NAME"] == request.form.get("OLD_SERVER_NAME", ""):
|
||||
flash(
|
||||
"The service was not edited because no values were changed.",
|
||||
"error",
|
||||
)
|
||||
if (config.get(f"{server_name}_IS_DRAFT", "no") == "yes") == is_draft and request.form["operation"] == "edit" and len(variables) == 1 and "SERVER_NAME" in variables and variables["SERVER_NAME"] == request.form.get("OLD_SERVER_NAME", ""):
|
||||
flash("The service was not edited because no values were changed.", "error")
|
||||
return redirect(url_for("loading", next=url_for("services")))
|
||||
elif request.form["operation"] == "new" and not variables:
|
||||
flash("The service was not created because all values had the default value.", "error")
|
||||
|
|
@ -767,22 +764,22 @@ def services():
|
|||
target=manage_bunkerweb,
|
||||
name="Reloading instances",
|
||||
args=("services", variables, request.form.get("OLD_SERVER_NAME", ""), variables.get("SERVER_NAME", "")),
|
||||
kwargs={"operation": request.form["operation"]},
|
||||
kwargs={"operation": request.form["operation"], "is_draft": is_draft},
|
||||
).start()
|
||||
|
||||
message = ""
|
||||
|
||||
if request.form["operation"] == "new":
|
||||
message = f"Creating service {variables.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
message = f"Creating {'draft ' if is_draft else ''}service {variables.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
elif request.form["operation"] == "edit":
|
||||
message = f"Saving configuration for service {request.form.get('OLD_SERVER_NAME', '').split(' ')[0]}"
|
||||
message = f"Saving configuration for {'draft ' if is_draft else ''}service {request.form.get('OLD_SERVER_NAME', '').split(' ')[0]}"
|
||||
elif request.form["operation"] == "delete":
|
||||
message = f"Deleting service {request.form.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
message = f"Deleting {'draft ' if is_draft else ''}service {request.form.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
|
||||
return redirect(url_for("loading", next=url_for("services"), message=message))
|
||||
|
||||
# Display services
|
||||
services = app.config["CONFIG"].get_services()
|
||||
services = app.config["CONFIG"].get_services(with_drafts=True)
|
||||
return render_template(
|
||||
"services.html",
|
||||
services=[
|
||||
|
|
@ -792,6 +789,7 @@ def services():
|
|||
"full_value": service["SERVER_NAME"]["value"],
|
||||
"method": service["SERVER_NAME"]["method"],
|
||||
},
|
||||
"IS_DRAFT": service.pop("IS_DRAFT", "no"),
|
||||
"USE_REVERSE_PROXY": service["USE_REVERSE_PROXY"],
|
||||
"SERVE_FILES": service["SERVE_FILES"],
|
||||
"REMOTE_PHP": service["REMOTE_PHP"],
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class Config:
|
|||
def get_settings(self) -> dict:
|
||||
return self.__settings
|
||||
|
||||
def get_config(self, methods: bool = True) -> dict:
|
||||
def get_config(self, methods: bool = True, with_drafts: bool = False) -> dict:
|
||||
"""Get the nginx variables env file and returns it as a dict
|
||||
|
||||
Returns
|
||||
|
|
@ -106,9 +106,9 @@ class Config:
|
|||
dict
|
||||
The nginx variables env file as a dict
|
||||
"""
|
||||
return self.__db.get_config(methods=methods)
|
||||
return self.__db.get_config(methods=methods, with_drafts=with_drafts)
|
||||
|
||||
def get_services(self, methods: bool = True) -> list[dict]:
|
||||
def get_services(self, methods: bool = True, with_drafts: bool = False) -> list[dict]:
|
||||
"""Get nginx's services
|
||||
|
||||
Returns
|
||||
|
|
@ -116,7 +116,7 @@ class Config:
|
|||
list
|
||||
The services
|
||||
"""
|
||||
return self.__db.get_services_settings(methods=methods)
|
||||
return self.__db.get_services_settings(methods=methods, with_drafts=with_drafts)
|
||||
|
||||
def check_variables(self, variables: dict, _global: bool = False) -> int:
|
||||
"""Testify that the variables passed are valid
|
||||
|
|
@ -163,7 +163,7 @@ class Config:
|
|||
def reload_config(self) -> None:
|
||||
self.__gen_conf(self.get_config(methods=False), self.get_services(methods=False))
|
||||
|
||||
def new_service(self, variables: dict) -> Tuple[str, int]:
|
||||
def new_service(self, variables: dict, is_draft: bool = False) -> Tuple[str, int]:
|
||||
"""Creates a new service from the given variables
|
||||
|
||||
Parameters
|
||||
|
|
@ -181,23 +181,17 @@ class Config:
|
|||
Exception
|
||||
raise this if the service already exists
|
||||
"""
|
||||
services = self.get_services(methods=False)
|
||||
services = self.get_services(methods=False, with_drafts=True)
|
||||
server_name_splitted = variables["SERVER_NAME"].split(" ")
|
||||
for service in services:
|
||||
if service["SERVER_NAME"] == variables["SERVER_NAME"] or service["SERVER_NAME"] in server_name_splitted:
|
||||
return (
|
||||
f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.",
|
||||
1,
|
||||
)
|
||||
return f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.", 1
|
||||
|
||||
services.append(variables)
|
||||
self.__gen_conf(self.get_config(methods=False), services)
|
||||
return (
|
||||
f"Configuration for {variables['SERVER_NAME'].split(' ')[0]} has been generated.",
|
||||
0,
|
||||
)
|
||||
services.append(variables | {"IS_DRAFT": "yes" if is_draft else "no"})
|
||||
self.__gen_conf(self.get_config(methods=False), services, check_changes=not is_draft)
|
||||
return f"Configuration for {variables['SERVER_NAME'].split(' ')[0]} has been generated.", 0
|
||||
|
||||
def edit_service(self, old_server_name: str, variables: dict, *, check_changes: bool = True) -> Tuple[str, int]:
|
||||
def edit_service(self, old_server_name: str, variables: dict, *, check_changes: bool = True, is_draft: bool = False) -> Tuple[str, int]:
|
||||
"""Edits a service
|
||||
|
||||
Parameters
|
||||
|
|
@ -212,22 +206,19 @@ class Config:
|
|||
str
|
||||
the confirmation message
|
||||
"""
|
||||
services = self.get_services(methods=False)
|
||||
services = self.get_services(methods=False, with_drafts=True)
|
||||
changed_server_name = old_server_name != variables["SERVER_NAME"]
|
||||
server_name_splitted = variables["SERVER_NAME"].split(" ")
|
||||
old_server_name_splitted = old_server_name.split(" ")
|
||||
for i, service in enumerate(deepcopy(services)):
|
||||
if service["SERVER_NAME"] == variables["SERVER_NAME"] or service["SERVER_NAME"] in server_name_splitted:
|
||||
if changed_server_name and service["SERVER_NAME"].split(" ")[0] != old_server_name_splitted[0]:
|
||||
return (
|
||||
f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.",
|
||||
1,
|
||||
)
|
||||
return f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.", 1
|
||||
services.pop(i)
|
||||
elif changed_server_name and (service["SERVER_NAME"] == old_server_name or service["SERVER_NAME"] in old_server_name_splitted):
|
||||
services.pop(i)
|
||||
|
||||
services.append(variables)
|
||||
services.append(variables | {"IS_DRAFT": "yes" if is_draft else "no"})
|
||||
config = self.get_config(methods=False)
|
||||
|
||||
if changed_server_name and server_name_splitted[0] != old_server_name_splitted[0]:
|
||||
|
|
@ -236,10 +227,7 @@ class Config:
|
|||
config.pop(k)
|
||||
|
||||
self.__gen_conf(config, services, check_changes=check_changes)
|
||||
return (
|
||||
f"Configuration for {old_server_name_splitted[0]} has been edited.",
|
||||
0,
|
||||
)
|
||||
return f"Configuration for {old_server_name_splitted[0]} has been edited.", 0
|
||||
|
||||
def edit_global_conf(self, variables: dict) -> str:
|
||||
"""Edits the global conf
|
||||
|
|
@ -277,7 +265,7 @@ class Config:
|
|||
"""
|
||||
service_name = service_name.split(" ")[0]
|
||||
full_env = self.get_config(methods=False)
|
||||
services = self.get_services(methods=False)
|
||||
services = self.get_services(methods=False, with_drafts=True)
|
||||
new_services = []
|
||||
found = False
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue