Toutes les app sur les cotés

This commit is contained in:
para
2026-09-25 00:02:53 +02:00
parent 455988e49c
commit 93dcf514d2
5 changed files with 262 additions and 132 deletions
+23
View File
@@ -10,6 +10,9 @@ import shutil
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from yunohost_sync import build_app_list, fetch_remote_apps, known_urls, load_env, sync_target
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
SCRIPT_DIR = Path(__file__).resolve().parent
@@ -71,6 +74,10 @@ class ReloadingHandler(http.server.SimpleHTTPRequestHandler):
self.wfile.write(body)
return
if self.path == "/__catalog":
self._send_catalog()
return
if self.path in ("/", "/index.html"):
try:
html = (SRC_DIR / "index.html").read_bytes()
@@ -114,6 +121,22 @@ class ReloadingHandler(http.server.SimpleHTTPRequestHandler):
self.send_error(404)
def _send_catalog(self):
env = load_env()
ssh_target, domain, self_app_id = sync_target(env)
if not ssh_target or not domain:
self._send_json({"error": "SYNC_SSH_HOST (ou REMOTE_HOST) et PORTAL_DOMAIN manquants dans .env"})
return
try:
with open(SRC_DIR / "config.json", encoding="utf-8") as f:
config = json.load(f)
remote = fetch_remote_apps(ssh_target, domain)
apps = build_app_list(remote, domain, self_app_id, exclude_urls=known_urls(config))
except Exception as exc: # SSH/JSON/reseau : on remonte un message lisible
self._send_json({"error": str(exc)})
return
self._send_json({"apps": apps})
def _send_json(self, payload):
body = json.dumps(payload).encode()
self.send_response(200)
+18 -116
View File
@@ -10,44 +10,16 @@ PORTAL_DOMAIN, REMOTE_USER) ; les arguments en ligne de commande, s'ils sont
fournis, ont priorite sur .env.
"""
import json
import subprocess
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = SCRIPT_DIR.parent
CONFIG_PATH = PROJECT_ROOT / "src" / "config.json"
ENV_PATH = PROJECT_ROOT / ".env"
def load_env():
env = {}
if not ENV_PATH.exists():
return env
for line in ENV_PATH.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
env[key.strip()] = value.strip()
return env
sys.path.insert(0, str(Path(__file__).resolve().parent))
from yunohost_sync import CONFIG_PATH, build_app_list, fetch_remote_apps, known_urls, load_env, sync_target
ENV = load_env()
SYNC_SSH_USER = ENV.get("SYNC_SSH_USER", "root")
SYNC_SSH_HOST = ENV.get("SYNC_SSH_HOST") or ENV.get("REMOTE_HOST")
PORTAL_DOMAIN = ENV.get("PORTAL_DOMAIN") or ENV.get("REMOTE_HOST")
SELF_APP_ID = ENV.get("REMOTE_USER", "") # cette page elle-meme, jamais ajoutee
if len(sys.argv) > 1:
SSH_TARGET = sys.argv[1]
elif SYNC_SSH_HOST:
SSH_TARGET = f"{SYNC_SSH_USER}@{SYNC_SSH_HOST}"
else:
SSH_TARGET = None
DOMAIN = sys.argv[2] if len(sys.argv) > 2 else PORTAL_DOMAIN
arg_target = sys.argv[1] if len(sys.argv) > 1 else None
arg_domain = sys.argv[2] if len(sys.argv) > 2 else None
SSH_TARGET, DOMAIN, SELF_APP_ID = sync_target(ENV, arg_target, arg_domain)
if not SSH_TARGET or not DOMAIN:
print(
@@ -59,103 +31,33 @@ if not SSH_TARGET or not DOMAIN:
)
sys.exit(1)
PALETTE = [
"#1db9c3", "#ff5a1f", "#2094f3", "#ffc230", "#00a651", "#e6721a",
"#b71c1c", "#f39c12", "#0d3b66", "#3b8a8c", "#568d34", "#609926",
"#d24939", "#0066cc", "#8e44ad", "#16a085", "#c0392b", "#2c3e50",
]
def color_of(name):
h = 0
for ch in name:
h = (h * 31 + ord(ch)) & 0xFFFFFFFF
return PALETTE[h % len(PALETTE)]
def initials_of(name):
words = name.strip().split()
if len(words) == 1:
return words[0][:2].upper()
return (words[0][0] + words[1][0]).upper()
def fetch_remote_apps():
cmd = ["ssh", "-o", "BatchMode=yes", SSH_TARGET, f"cat /etc/yunohost/portal/{DOMAIN}.json"]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Erreur SSH: {result.stderr}", file=sys.stderr)
sys.exit(1)
return json.loads(result.stdout)
def normalize_url(url):
return url.replace("https://", "").replace("http://", "").rstrip("/").lower()
def known_urls(config):
urls = set()
for app in config.get("pinned", []):
urls.add(normalize_url(app["url"]))
for group in config.get("groups", []):
for app in group["apps"]:
urls.add(normalize_url(app["url"]))
for app in config.get("ungrouped", []):
urls.add(normalize_url(app["url"]))
return urls
def main():
with open(CONFIG_PATH, encoding="utf-8") as f:
config = json.load(f)
remote = fetch_remote_apps()
remote_apps = remote.get("apps", {})
try:
remote = fetch_remote_apps(SSH_TARGET, DOMAIN)
except (RuntimeError, json.JSONDecodeError) as exc:
print(f"Erreur SSH: {exc}", file=sys.stderr)
sys.exit(1)
existing_urls = known_urls(config)
to_add = build_app_list(remote, DOMAIN, SELF_APP_ID, exclude_urls=existing_urls)
added = []
for perm_key, info in sorted(remote_apps.items()):
if not perm_key.endswith(".main"):
continue
app_id = perm_key[: -len(".main")]
if app_id == SELF_APP_ID:
continue
url = info["url"]
if not url.startswith("http"):
url = "https://" + url
if not url.endswith("/"):
url += "/"
if normalize_url(url) in existing_urls:
continue
name = info.get("label", app_id)
app = {
"id": app_id,
"name": name,
"url": url,
"color": color_of(name),
"initials": initials_of(name),
}
logo = info.get("logo")
if logo:
app["icon"] = f"https://{DOMAIN}{logo}"
config.setdefault("ungrouped", []).append(app)
added.append(name)
if not added:
if not to_add:
print("Rien a ajouter, config.json est deja a jour.")
return
config.setdefault("ungrouped", []).extend(to_add)
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)
f.write("\n")
print(f"{len(added)} application(s) ajoutee(s) dans \"ungrouped\" :")
for name in added:
print(f" - {name}")
print(f"{len(to_add)} application(s) ajoutee(s) dans \"ungrouped\" :")
for app in to_add:
print(f" - {app['name']}")
if __name__ == "__main__":