HEX
Server: Apache/2.4.63 (Unix)
System: Linux TOMS-220NAS 4.4.302+ #86009 SMP Wed Nov 26 18:19:17 CST 2025 x86_64
User: flavio87 (1026)
PHP: 8.3.27
Disabled: NONE
Upload Files
File: //var/packages/SupportService/var/actions/SupportService/upload-debug-dat/css_server_api.py
import typing
from enum import Enum
import urllib.request
import urllib.parse
import time
import json
import os
from .multipart_data_encoder import encode_multipart_data

UPLOAD_DEBUG_DAT_CONF = "/var/packages/SupportService/target/configs/upload_debug_dat.conf"


class ResultForCssServer(Enum):
    SUCCESS = 1
    GENERATE_DEBUG_DAT_FAIL = 2
    UPLOAD_FAIL = 3


class ApiPsFilesFail(Exception):
    pass


class ApiPsResultsFail(Exception):
    pass


class CssServerApi(object):
    def __init__(self, token: str, serial_number: str) -> None:
        self.token = token
        self.serial_number = serial_number
        css_server_url = self._get_css_server_url()
        if css_server_url is None:
            raise Exception("failed to get css_server.url in config")
        self.css_server_url = css_server_url

    def _get_css_server_url(self) -> typing.Optional[str]:
        if not os.path.exists(UPLOAD_DEBUG_DAT_CONF):
            # backward compatibility for old SupportService without config file
            return "https://supapi.synology.com"
        with open(UPLOAD_DEBUG_DAT_CONF, "r", encoding="utf-8") as f:
            return json.load(f).get("debug_dat_file_transmitter", {}).get("css_server", {}).get("url", None)

    def _post(self, url_rule: str, headers: dict, data: bytes, retry_limit: int) -> typing.Tuple[typing.Optional[int], typing.Optional[str]]:
        request = urllib.request.Request(urllib.parse.urljoin(self.css_server_url, url_rule), data=data, headers=headers, method="POST")

        for _ in range(retry_limit):
            try:
                response = urllib.request.urlopen(request, timeout=1800)
                status = response.status
                body = response.read().decode()
                return status, body
            except Exception:
                time.sleep(3)
        return None, None

    def send_ps_files(self, debug_dat_path: str, app_list: typing.List[str]):
        fields = {f"debug_data_apps[{index}]": app for index, app in enumerate(app_list)}
        fields["serial_number"] = self.serial_number
        files = [("debug_file", debug_dat_path)]
        body, content_type = encode_multipart_data(fields, files)
        headers = {"AUTH-TOKEN": self.token, "Content-Type": content_type}
        if self._post("/active_insight/ps_files", headers, body, retry_limit=5) == (None, None):
            raise ApiPsFilesFail

    def send_ps_results(self, reason_for_css: ResultForCssServer):
        if reason_for_css == ResultForCssServer.SUCCESS:
            status = "Success"
        elif reason_for_css == ResultForCssServer.GENERATE_DEBUG_DAT_FAIL:
            status = "Failed to generate debug file"
        elif reason_for_css == ResultForCssServer.UPLOAD_FAIL:
            status = "Failed to upload debug file to server"
        headers = {"AUTH-TOKEN": self.token, "Content-Type": "application/json"}
        data = json.dumps({"status": status, "serial_number": self.serial_number}).encode("utf-8")
        if self._post("/active_insight/ps_results", headers, data, retry_limit=5) == (None, None):
            raise ApiPsResultsFail