update domain.py to new config panel

This commit is contained in:
axolotle 2023-02-21 17:21:51 +01:00
parent 807f5956ea
commit 0778cf3ffe
4 changed files with 79 additions and 105 deletions

View file

@ -5,10 +5,10 @@ i18n = "domain_config"
name = "Features" name = "Features"
[feature.app] [feature.app]
[feature.app.default_app] [feature.app.default_app]
type = "app" type = "app"
filter = "is_webapp" filter = "is_webapp"
default = "_none"
[feature.mail] [feature.mail]
@ -44,28 +44,30 @@ name = "Certificate"
[cert.cert.cert_validity] [cert.cert.cert_validity]
type = "number" type = "number"
readonly = true readonly = true
visible = "false" visible = false
# Automatically filled by DomainConfigPanel # Automatically filled by DomainConfigPanel
[cert.cert.cert_issuer] [cert.cert.cert_issuer]
type = "string" type = "string"
readonly = true
visible = false visible = false
# Automatically filled by DomainConfigPanel # Automatically filled by DomainConfigPanel
[cert.cert.acme_eligible] [cert.cert.acme_eligible]
type = "boolean" type = "boolean"
readonly = true
visible = false visible = false
# Automatically filled by DomainConfigPanel # Automatically filled by DomainConfigPanel
[cert.cert.acme_eligible_explain] [cert.cert.acme_eligible_explain]
type = "alert" type = "alert"
style = "warning" style = "warning"
visible = "acme_eligible == false || acme_eligible == null" visible = "!acme_eligible"
[cert.cert.cert_no_checks] [cert.cert.cert_no_checks]
type = "boolean" type = "boolean"
default = false default = false
visible = "acme_eligible == false || acme_eligible == null" visible = "!acme_eligible"
[cert.cert.cert_install] [cert.cert.cert_install]
type = "button" type = "button"

View file

@ -1411,7 +1411,7 @@ def app_remove(operation_logger, app, purge=False, force_workdir=None):
for domain in domain_list()["domains"]: for domain in domain_list()["domains"]:
if domain_config_get(domain, "feature.app.default_app") == app: if domain_config_get(domain, "feature.app.default_app") == app:
domain_config_set(domain, "feature.app.default_app", "_none") domain_config_set(domain, "feature.app.default_app", None)
if ret == 0: if ret == 0:
logger.success(m18n.n("app_removed", app=app)) logger.success(m18n.n("app_removed", app=app))
@ -1448,7 +1448,7 @@ def app_makedefault(operation_logger, app, domain=None, undo=False):
operation_logger.start() operation_logger.start()
if undo: if undo:
domain_config_set(domain, "feature.app.default_app", "_none") domain_config_set(domain, "feature.app.default_app", None)
else: else:
domain_config_set(domain, "feature.app.default_app", app) domain_config_set(domain, "feature.app.default_app", app)
@ -1701,7 +1701,7 @@ def app_ssowatconf():
for domain in domains: for domain in domains:
default_app = domain_config_get(domain, "feature.app.default_app") default_app = domain_config_get(domain, "feature.app.default_app")
if default_app != "_none" and _is_installed(default_app): if default_app and _is_installed(default_app):
app_settings = _get_app_settings(default_app) app_settings = _get_app_settings(default_app)
app_domain = app_settings["domain"] app_domain = app_settings["domain"]
app_path = app_settings["path"] app_path = app_settings["path"]

View file

@ -529,7 +529,7 @@ def _get_registrar_config_section(domain):
parent_domain=parent_domain, parent_domain=parent_domain,
parent_domain_link=parent_domain_link, parent_domain_link=parent_domain_link,
), ),
"value": "parent_domain", "default": "parent_domain",
} }
) )
return OrderedDict(registrar_infos) return OrderedDict(registrar_infos)
@ -542,7 +542,7 @@ def _get_registrar_config_section(domain):
"type": "alert", "type": "alert",
"style": "success", "style": "success",
"ask": m18n.n("domain_dns_registrar_yunohost"), "ask": m18n.n("domain_dns_registrar_yunohost"),
"value": "yunohost", "default": "yunohost",
} }
) )
return OrderedDict(registrar_infos) return OrderedDict(registrar_infos)
@ -552,7 +552,7 @@ def _get_registrar_config_section(domain):
"type": "alert", "type": "alert",
"style": "info", "style": "info",
"ask": m18n.n("domain_dns_conf_special_use_tld"), "ask": m18n.n("domain_dns_conf_special_use_tld"),
"value": None, "default": None,
} }
) )
@ -564,7 +564,7 @@ def _get_registrar_config_section(domain):
"type": "alert", "type": "alert",
"style": "warning", "style": "warning",
"ask": m18n.n("domain_dns_registrar_not_supported"), "ask": m18n.n("domain_dns_registrar_not_supported"),
"value": None, "default": None,
} }
) )
else: else:
@ -573,7 +573,7 @@ def _get_registrar_config_section(domain):
"type": "alert", "type": "alert",
"style": "info", "style": "info",
"ask": m18n.n("domain_dns_registrar_supported", registrar=registrar), "ask": m18n.n("domain_dns_registrar_supported", registrar=registrar),
"value": registrar, "default": registrar,
} }
) )

View file

@ -18,7 +18,7 @@
# #
import os import os
import time import time
from typing import List, Optional from typing import List, Optional, TYPE_CHECKING, Union, Any
from collections import OrderedDict from collections import OrderedDict
from moulinette import m18n, Moulinette from moulinette import m18n, Moulinette
@ -33,10 +33,14 @@ from yunohost.app import (
_get_conflicting_apps, _get_conflicting_apps,
) )
from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf
from yunohost.utils.config import ConfigPanel, Question from yunohost.utils.configpanel import Config
from yunohost.utils.config import Question
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
if TYPE_CHECKING:
from yunohost.utils.configpanel import YunoForm
logger = getActionLogger("yunohost.domain") logger = getActionLogger("yunohost.domain")
DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains" DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains"
@ -532,135 +536,103 @@ def domain_config_set(
return config.set(key, value, args, args_file, operation_logger=operation_logger) return config.set(key, value, args, args_file, operation_logger=operation_logger)
class DomainConfigPanel(ConfigPanel): class DomainConfigPanel(Config):
entity_type = "domain" entity_type = "domain"
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml" save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
save_mode = "diff" save_mode = "diff"
def _apply(self): # TODO add mechanism to share some settings with other domains on the same zone
if (
"default_app" in self.future_values
and self.future_values["default_app"] != self.values["default_app"]
):
from yunohost.app import app_ssowatconf, app_map
if "/" in app_map(raw=True).get(self.entity, {}): def _get_config_data(
raise YunohostValidationError( self,
"app_make_default_location_already_used", panel_id: Union[str, None] = None,
app=self.future_values["default_app"], section_id: Union[str, None] = None,
domain=self.entity, option_id: Union[str, None] = None,
other_app=app_map(raw=True)[self.entity]["/"]["id"], ) -> dict[str, Any]:
) any_filter = all([panel_id, section_id, option_id])
config_data = super()._get_config_data(panel_id, section_id, option_id)
super()._apply() config_data["feature"]["xmpp"]["xmpp"]["default"] = (
# Reload ssowat if default app changed
if (
"default_app" in self.future_values
and self.future_values["default_app"] != self.values["default_app"]
):
app_ssowatconf()
stuff_to_regen_conf = []
if (
"xmpp" in self.future_values
and self.future_values["xmpp"] != self.values["xmpp"]
):
stuff_to_regen_conf.append("nginx")
stuff_to_regen_conf.append("metronome")
if (
"mail_in" in self.future_values
and self.future_values["mail_in"] != self.values["mail_in"]
) or (
"mail_out" in self.future_values
and self.future_values["mail_out"] != self.values["mail_out"]
):
if "nginx" not in stuff_to_regen_conf:
stuff_to_regen_conf.append("nginx")
stuff_to_regen_conf.append("postfix")
stuff_to_regen_conf.append("dovecot")
stuff_to_regen_conf.append("rspamd")
if stuff_to_regen_conf:
regen_conf(names=stuff_to_regen_conf)
def _get_toml(self):
toml = super()._get_toml()
toml["feature"]["xmpp"]["xmpp"]["default"] = (
1 if self.entity == _get_maindomain() else 0 1 if self.entity == _get_maindomain() else 0
) )
# Optimize wether or not to load the DNS section, # Optimize wether or not to load the DNS section,
# e.g. we don't want to trigger the whole _get_registary_config_section # e.g. we don't want to trigger the whole _get_registary_config_section
# when just getting the current value from the feature section # when just getting the current value from the feature section
filter_key = self.filter_key.split(".") if self.filter_key != "" else [] if not any_filter or panel_id == "dns":
if not filter_key or filter_key[0] == "dns":
from yunohost.dns import _get_registrar_config_section from yunohost.dns import _get_registrar_config_section
toml["dns"]["registrar"] = _get_registrar_config_section(self.entity) config_data["dns"]["registrar"] = _get_registrar_config_section(self.entity)
# FIXME: Ugly hack to save the registar id/value and reinject it in _load_current_values ...
self.registar_id = toml["dns"]["registrar"]["registrar"]["value"]
del toml["dns"]["registrar"]["registrar"]["value"]
# Cert stuff # Cert stuff
if not filter_key or filter_key[0] == "cert": if not any_filter or panel_id == "cert":
from yunohost.certificate import certificate_status from yunohost.certificate import certificate_status
status = certificate_status([self.entity], full=True)["certificates"][ status = certificate_status([self.entity], full=True)["certificates"][
self.entity self.entity
] ]
toml["cert"]["cert"]["cert_summary"]["style"] = status["style"] config_data["cert"]["cert"]["cert_summary"]["style"] = status["style"]
# i18n: domain_config_cert_summary_expired # i18n: domain_config_cert_summary_expired
# i18n: domain_config_cert_summary_selfsigned # i18n: domain_config_cert_summary_selfsigned
# i18n: domain_config_cert_summary_abouttoexpire # i18n: domain_config_cert_summary_abouttoexpire
# i18n: domain_config_cert_summary_ok # i18n: domain_config_cert_summary_ok
# i18n: domain_config_cert_summary_letsencrypt # i18n: domain_config_cert_summary_letsencrypt
toml["cert"]["cert"]["cert_summary"]["ask"] = m18n.n( config_data["cert"]["cert"]["cert_summary"]["ask"] = m18n.n(
f"domain_config_cert_summary_{status['summary']}" f"domain_config_cert_summary_{status['summary']}"
) )
# FIXME: Ugly hack to save the cert status and reinject it in _load_current_values ... for option_id, status_key in [
self.cert_status = status ("cert_validity", "validity"),
("cert_issuer", "CA_type"),
("acme_eligible", "ACME_eligible"),
# FIXME not sure why "summary" was injected in settings values
# ("summary", "summary")
]:
config_data["cert"]["cert"][option_id]["default"] = status[status_key]
return toml # Other specific strings used in config panels
def get(self, key="", mode="classic"):
result = super().get(key=key, mode=mode)
if mode == "full":
for panel, section, option in self._iterate():
# This injects:
# i18n: domain_config_cert_renew_help # i18n: domain_config_cert_renew_help
# i18n: domain_config_default_app_help
# i18n: domain_config_xmpp_help return config_data
if m18n.key_exists(self.config["i18n"] + "_" + option["id"] + "_help"):
option["help"] = m18n.n( def _apply(self, settings: "YunoForm", exclude=None):
self.config["i18n"] + "_" + option["id"] + "_help" # Do not rely on super `_apply` to get new values since it excludes
# values that are default values.
new_values = settings.dict(exclude_unset=True)
# Check if another app is already the default one
# FIXME could be a custom pydantic.validator() instead
if "default_app" in new_values:
from yunohost.app import app_ssowatconf, app_map
app_map_ = app_map(raw=True).get(self.entity, {})
if "/" in app_map_:
raise YunohostValidationError(
"app_make_default_location_already_used",
app=new_values["default_app"],
domain=self.entity,
other_app=app_map_["/"]["id"],
) )
return self.config
return result # Save settings (without default values)
super()._apply(settings)
def _load_current_values(self): # Reload ssowat if default app changed
# TODO add mechanism to share some settings with other domains on the same zone if "default_app" in new_values:
super()._load_current_values() app_ssowatconf()
# FIXME: Ugly hack to save the registar id/value and reinject it in _load_current_values ... stuff_to_regen_conf = set()
filter_key = self.filter_key.split(".") if self.filter_key != "" else [] if "xmpp" in new_values:
if not filter_key or filter_key[0] == "dns": stuff_to_regen_conf.update({"nginx", "metronome"})
self.values["registrar"] = self.registar_id
# FIXME: Ugly hack to save the cert status and reinject it in _load_current_values ... if "mail_in" in new_values or "mail_out" in new_values:
if not filter_key or filter_key[0] == "cert": stuff_to_regen_conf.update({"nginx", "postfix", "dovecot", "rspamd"})
self.values["cert_validity"] = self.cert_status["validity"]
self.values["cert_issuer"] = self.cert_status["CA_type"] if stuff_to_regen_conf:
self.values["acme_eligible"] = self.cert_status["ACME_eligible"] # TODO `regen_conf` could (only?) accept a set() as "names"
self.values["summary"] = self.cert_status["summary"] regen_conf(names=list(stuff_to_regen_conf))
def domain_action_run(domain, action, args=None): def domain_action_run(domain, action, args=None):