import os import json import pathlib import shutil import subprocess from tempfile import mkstemp from shutil import move, copymode from os import fdopen, remove from threading import Thread from libs.noSys.noSysModule import NoSysModule from libs.vueNoSys.vueNoSysApiBlueprint import Blueprint from libs.app.common.logging import get_logger from libs.app.common.paths import ROOT_DIR from libs.app.common.network_utils import check_url logger = get_logger() class VueNoSys(NoSysModule): def __init__(self, nosys_core): super().__init__(nosys_core) self.server_port = self.config.get("server", {"port":"3001"}).get("port", "3001") self.npm_cmd = "npm.cmd" if os.name == "nt" else "npm" self.npm_cwd = os.path.join(pathlib.Path(__file__).parent.resolve()) def setup(self): self.nosys_core.modules.api.register_blueprint(Blueprint(self).blueprint) self.move_modules_vue_files() self.install_modules_dependencies() self.config_env() self.check_npm_version() if self.config.get("server", {"enabled":False}).get("enabled", False): self.run_npm_dev() self.run_npm_build() def on_nosys_ready(self, event): pass def check_npm_version(self): try: out = subprocess.check_output([self.npm_cmd, "--version"], shell=True, cwd=self.npm_cwd) except subprocess.CalledProcessError: # TODO Open npm download page logger.exception("Command NPM version failed. Please install Node.js to fix this error. https://nodejs.org/en/download") return None logger.debug(f"NPM Version {out.decode()}") def run_npm_dev(self): try: dev_server_url = f"https://localhost:{self.server_port}" if check_url(dev_server_url, timeout=3, retries=2): logger.debug(f"Not starting dev server - HTTP already running in {dev_server_url}") return subprocess.check_call([self.npm_cmd, "install"], shell=True, cwd=self.npm_cwd) flag = subprocess.CREATE_NEW_CONSOLE # subprocess.CREATE_NO_WINDOW npm_process = subprocess.Popen([self.npm_cmd, "run", "dev"], cwd=self.npm_cwd, creationflags=flag) logger.debug(f"NPM dev server started, PID={npm_process.pid}") except: logger.exception(f"Failed to start npm dev server") def run_npm_build(self): try: subprocess.check_call([self.npm_cmd, "install"], cwd=self.npm_cwd) subprocess.check_call([self.npm_cmd, "run", "build"], cwd=self.npm_cwd) logger.info("Vue build completed successfully") except: logger.exception(f"Failed to build Vue frontend") return None return os.path.join(self.npm_cwd, "dist") def install_modules_dependencies(self, lib_id=None): modules_path = os.path.join(pathlib.Path(__file__).parent.resolve(), 'src', 'modules') modules = [lib_id] if lib_id else os.listdir(modules_path) for module in modules: module_path = os.path.join(modules_path, module) dep_file = os.path.join(module_path, "dependencies.txt") if os.path.exists(dep_file): with open(dep_file, "r") as f: dependencies = [ line.strip() for line in f.readlines() if line.strip() ] if dependencies: logger.debug(f"Installing dependencies for {module}: {dependencies}") try: subprocess.run( ["npm.cmd", "install"] + dependencies, cwd=self.npm_cwd, check=True ) except: logger.exception(f"Error installing dependencies for module {module}") else: logger.debug(f"Module {module} has no dependencies.txt") def move_modules_vue_files(self): for lib in self.nosys_core.config.get("app", "libs"): lib_id = lib.get("id") frontend = self.nosys_core.config.get(lib_id, "info", "frontend") if frontend == 'vue': from_path = os.path.join(ROOT_DIR, 'libs', lib_id, 'vue') to_path = os.path.join(pathlib.Path(__file__).parent.resolve(), 'src', 'modules', lib_id) if(os.path.exists(from_path)): if(os.path.exists(to_path)): shutil.rmtree(to_path) pathlib.Path(to_path).mkdir(parents=True, exist_ok=True) try: for file_name in os.listdir(from_path): shutil.move(os.path.join(from_path, file_name), os.path.join(to_path, file_name)) shutil.rmtree(from_path) except Exception as e: logger.error(f'Error while moving files from {os.path.join(from_path, file_name)} to {os.path.join(to_path, file_name)}: {e}') def config_env(self): api_config = self.nosys_core.config.get("api") env_file = os.path.join(pathlib.Path(__file__).parent.resolve(), '.env') fh, abs_path = mkstemp() with fdopen(fh,'w') as new_file: with open(env_file) as old_file: for line in old_file: if "VITE_PORT=" in line: line = f"VITE_PORT={self.server_port}\n" elif "VITE_BACKEND_API_HOST=" in line: vite_backend_api_host = api_config["server"]["host"] line = f"VITE_BACKEND_API_HOST=https://{vite_backend_api_host}\n" elif "VITE_BACKEND_API_PORT=" in line: vite_backend_api_port = api_config["server"]["port"] line = f"VITE_BACKEND_API_PORT={vite_backend_api_port}\n" new_file.write(line) copymode(env_file, abs_path) remove(env_file) move(abs_path, env_file)