From 44e6c866c00783389bf080e95d7e5be2071eded6 Mon Sep 17 00:00:00 2001 From: quietsy Date: Sun, 9 Feb 2025 17:37:12 +0200 Subject: [PATCH] Add even more fields --- root/app/keyvaluestore.py | 3 + root/app/models.py | 80 +++++++++++++++++++++++- root/app/updater.py | 125 +++++++++++++++++++++++++++++++++++--- 3 files changed, 198 insertions(+), 10 deletions(-) diff --git a/root/app/keyvaluestore.py b/root/app/keyvaluestore.py index 4185249..ac89702 100644 --- a/root/app/keyvaluestore.py +++ b/root/app/keyvaluestore.py @@ -38,6 +38,9 @@ class KeyValueStore(dict): def __getitem__(self, key): item = self.conn.execute("SELECT value FROM kv WHERE key = ?", (key,)).fetchone() return item[0] if item else None + def get_updated_at(self, key): + item = self.conn.execute("SELECT updated_at FROM kv WHERE key = ?", (key,)).fetchone() + return item[0] if item else None def set_value(self, key, value, schema_version): self.conn.execute("REPLACE INTO kv (key, value, updated_at, schema_version) VALUES (?, ?, CURRENT_TIMESTAMP, ?)", (key, value, schema_version)) self.conn.commit() diff --git a/root/app/models.py b/root/app/models.py index ddb4c98..a2c7037 100644 --- a/root/app/models.py +++ b/root/app/models.py @@ -15,20 +15,93 @@ class Changelog(BaseModel): date: str desc: str +class Volume(BaseModel): + path: str + host_path: str + desc: str + optional: bool + +class Port(BaseModel): + external: str + internal: str + desc: str + optional: bool + +class EnvVar(BaseModel): + name: str + value: str + desc: str + optional: bool + +class EnvVar(BaseModel): + name: str + value: str + desc: str + optional: bool + +class Custom(BaseModel): + name: str + name_compose: str + value: str + desc: str + optional: bool + +class SecurityOpt(BaseModel): + run_var: str + compose_var: str + desc: str + optional: bool + +class Device(BaseModel): + path: str + host_path: str + desc: str + optional: bool + +class Cap(BaseModel): + cap_add: str + desc: str + optional: bool + +class Hostname(BaseModel): + hostname: str + desc: str + optional: bool + +class MacAddress(BaseModel): + mac_address: str + desc: str + optional: bool + class Image(BaseModel): name: str github_url: str - project_url: str + project_url: str | None + project_logo: str | None + application_setup: str description: str version: str version_timestamp: str - changelog: list[Changelog] category: str stable: bool - deprecated: bool + deprecated: bool | None stars: int + readonly_supported: bool | None + nonroot_supported: bool | None + privileged: bool | None + networking: str | None + hostname: Hostname | None + mac_address: MacAddress | None tags: list[Tag] architectures: list[Architecture] + env_vars: list[EnvVar] | None + volumes: list[Volume] | None + ports: list[Port] | None + custom: list[Custom] | None + security_opt: list[SecurityOpt] | None + devices: list[Device] | None + caps: list[Cap] | None + changelog: list[Changelog] | None class Repository(BaseModel): linuxserver: list[Image] @@ -38,4 +111,5 @@ class ImagesData(BaseModel): class ImagesResponse(BaseModel): status: str + last_updated: str data: ImagesData diff --git a/root/app/updater.py b/root/app/updater.py index 80a20a4..514ea15 100644 --- a/root/app/updater.py +++ b/root/app/updater.py @@ -1,6 +1,8 @@ import lsio_github as gh from keyvaluestore import KeyValueStore, set_db_schema -from models import Architecture, Changelog, Tag, Image, Repository, ImagesData, ImagesResponse, IMAGES_SCHEMA_VERSION +from models import Architecture, Changelog, Tag, EnvVar, Volume, Port +from models import Custom, SecurityOpt, Device, Cap, Hostname, MacAddress, Image +from models import Repository, ImagesData, ImagesResponse, IMAGES_SCHEMA_VERSION import datetime import os @@ -31,7 +33,7 @@ def get_architectures(readme_vars): def get_changelogs(readme_vars): if "changelogs" not in readme_vars: - return [Changelog(date="01.01.50", desc="No changelog")] + return None changelogs = [] for item in readme_vars["changelogs"][0:3]: changelogs.append(Changelog(date=item["date"][0:-1], desc=item["desc"])) @@ -47,6 +49,98 @@ def get_description(readme_vars): description = description.replace("({{ project_url }})", "") return description +def get_env_vars(readme_vars): + env_vars = [] + if readme_vars.get("common_param_env_vars_enabled", False): + env_vars.extend([ + EnvVar(name="PUID", value="1000", desc="User ID", optional=False), + EnvVar(name="PGID", value="1000", desc="Group ID", optional=False), + EnvVar(name="TZ", value="Etc/UTC", desc="Timezone", optional=False), + ]) + if "param_env_vars" in readme_vars: + for item in readme_vars["param_env_vars"]: + env_vars.append(EnvVar(name=item["env_var"], value=item["env_value"], desc=item["desc"], optional=False)) + if "opt_param_env_vars" in readme_vars: + for item in readme_vars["opt_param_env_vars"]: + env_vars.append(EnvVar(name=item["env_var"], value=item["env_value"], desc=item["desc"], optional=True)) + return env_vars if env_vars else None + +def get_volumes(readme_vars): + volumes = [] + if "param_volumes" in readme_vars: + for item in readme_vars["param_volumes"]: + volumes.append(Volume(path=item["vol_path"], host_path=item["vol_host_path"], desc=item["desc"], optional=False)) + if "opt_param_volumes" in readme_vars: + for item in readme_vars["opt_param_volumes"]: + volumes.append(Volume(path=item["vol_path"], host_path=item["vol_host_path"], desc=item["desc"], optional=True)) + return volumes if volumes else None + +def get_ports(readme_vars): + ports = [] + if "param_ports" in readme_vars: + for item in readme_vars["param_ports"]: + ports.append(Port(external=item["external_port"], internal=item["internal_port"], desc=item["port_desc"], optional=False)) + if "opt_param_ports" in readme_vars: + for item in readme_vars["opt_param_ports"]: + ports.append(Port(external=item["external_port"], internal=item["internal_port"], desc=item["port_desc"], optional=True)) + return ports if ports else None + +def get_custom(readme_vars): + custom = [] + if "custom_params" in readme_vars: + for item in readme_vars["custom_params"]: + custom.append(Custom(name=item["name"], name_compose=item["name_compose"], value=item["value"], desc=item["desc"], optional=False)) + if "opt_custom_params" in readme_vars: + for item in readme_vars["opt_custom_params"]: + custom.append(Custom(name=item["name"], name_compose=item["name_compose"], value=item["value"], desc=item["desc"], optional=True)) + return custom if custom else None + +def get_security_opt(readme_vars): + security_opts = [] + if "security_opt_param_vars" in readme_vars: + for item in readme_vars["security_opt_param_vars"]: + security_opts.append(SecurityOpt(run_var=item["run_var"], compose_var=item["compose_var"], desc=item["desc"], optional=False)) + if "opt_security_opt_param_vars" in readme_vars: + for item in readme_vars["opt_security_opt_param_vars"]: + security_opts.append(SecurityOpt(run_var=item["run_var"], compose_var=item["compose_var"], desc=item["desc"], optional=True)) + return security_opts if security_opts else None + +def get_devices(readme_vars): + devices = [] + if "param_devices" in readme_vars: + for item in readme_vars["param_devices"]: + devices.append(Device(path=item["device_path"], host_path=item["device_host_path"], desc=item["desc"], optional=False)) + if "opt_param_devices" in readme_vars: + for item in readme_vars["opt_param_devices"]: + devices.append(Device(path=item["device_path"], host_path=item["device_host_path"], desc=item["desc"], optional=True)) + return devices if devices else None + +def get_caps(readme_vars): + caps = [] + if "cap_add_param_vars" in readme_vars: + for item in readme_vars["cap_add_param_vars"]: + caps.append(Cap(cap_add=item["cap_add_var"], desc=item["desc"], optional=False)) + if "opt_cap_add_param_vars" in readme_vars: + for item in readme_vars["opt_cap_add_param_vars"]: + caps.append(Cap(cap_add=item["cap_add_var"], desc=item["desc"], optional=True)) + return caps if caps else None + +def get_hostname(readme_vars): + include_hostname = readme_vars.get("param_usage_include_hostname", False) + if not include_hostname: + return None + optional = include_hostname == "optional" + hostname = readme_vars.get("param_hostname", False).replace("{{ project_name }}", readme_vars["project_name"]) + return Hostname(hostname=hostname, desc=readme_vars.get("param_hostname_desc", ""), optional=optional) + +def get_mac_address(readme_vars): + include_mac_address = readme_vars.get("param_usage_include_mac_address", False) + if not include_mac_address: + return None + optional = include_mac_address == "optional" + hostname = readme_vars.get("param_mac_address", False) + return MacAddress(mac_address=hostname, desc=readme_vars.get("param_mac_address_desc", ""), optional=optional) + def get_image(repo): if not repo.name.startswith("docker-") or repo.name.startswith("docker-baseimage-"): return None @@ -57,12 +151,15 @@ def get_image(repo): if "Internal" in categories: return None tags, stable = get_tags(readme_vars) - deprecated = readme_vars.get("project_deprecation_status", False) + deprecated = readme_vars.get("project_deprecation_status", None) version, version_timestamp = gh.get_last_stable_release(repo) return Image( name=repo.name.replace("docker-", ""), github_url=repo.html_url, - project_url=readme_vars.get("project_url", ""), + stars=repo.stargazers_count, + project_url=readme_vars.get("project_url", None), + project_logo=readme_vars.get("project_logo", None), + application_setup=f"{repo.html_url}?tab=readme-ov-file#application-setup", description=get_description(readme_vars), version=version, version_timestamp=version_timestamp, @@ -70,9 +167,21 @@ def get_image(repo): category=categories, stable=stable, deprecated=deprecated, - stars=repo.stargazers_count, tags=tags, - architectures=get_architectures(readme_vars) + architectures=get_architectures(readme_vars), + readonly_supported=readme_vars.get("readonly_supported", None), + nonroot_supported=readme_vars.get("nonroot_supported", None), + privileged=readme_vars.get("privileged", None), + networking=readme_vars.get("param_net", None), + hostname=get_hostname(readme_vars), + mac_address=get_mac_address(readme_vars), + env_vars=get_env_vars(readme_vars), + volumes=get_volumes(readme_vars), + ports=get_ports(readme_vars), + custom=get_custom(readme_vars), + security_opt=get_security_opt(readme_vars), + devices=get_devices(readme_vars), + caps=get_caps(readme_vars), ) def update_images(): @@ -89,7 +198,9 @@ def update_images(): if not image: continue images.append(image) - new_state = ImagesResponse(status="OK", data=ImagesData(repositories=Repository(linuxserver=images))).model_dump_json() + data = ImagesData(repositories=Repository(linuxserver=images)) + response = ImagesResponse(status="OK", last_updated=kv.get_updated_at("images"), data=data) + new_state = response.model_dump_json(exclude_none=True) kv.set_value("images", new_state, IMAGES_SCHEMA_VERSION) print(f"{datetime.datetime.now()} - updated images")