import json from server import PromptServer from aiohttp import web from importlib import reload import logging import os from folder_paths import output_directory, input_directory, temp_directory from custom_nodes.EternalKernelLiteGraphNodes.local_shared import GDE_PATH, ETK_PATH from .update_js import check_for_js_extension fake_git = None real_git_url = "https://github.com/story-squad/GDE_Graph_IO.git" # is this actually an uri? class GitIO: def __init__(self, i_real_git_url: str, i_gde_path: str): self.gde_path = i_gde_path self.real_git_io_path = self.setup_git_io(i_real_git_url) def setup_git_io(self, l_real_git_url: str): real_git_path = os.path.join(self.gde_path, "data") real_git_path = os.path.join(real_git_path, "GDE_Graph_IO") # check to see if it exists and is a git repo, if not then clone it if os.path.exists(real_git_path): # check to see if it is a git repo if os.path.exists(os.path.join(real_git_path, ".git")): print("git io found") else: # not a git repo, delete it and clone it print("git io error, not a git repo") else: # clone it print("git io not found") print("cloning git io repo") os.system("git clone " + l_real_git_url + " " + real_git_path) return real_git_path def ensure_git_state(self, user: str): import subprocess """works in windows or linux, ensures that the git repo is in the correct state""" if not os.path.exists(self.real_git_io_path): raise Exception("git io path not found") if not os.path.exists(os.path.join(self.real_git_io_path, ".git")): raise Exception("git io error, not a git repo") def run_git_command(command: str): """Helper function to run a git command in the correct directory""" return subprocess.check_output(command, cwd=self.real_git_io_path, text=True).strip() # fetch first? run_git_command(["git", "fetch"]) # get the current branch current_branch = run_git_command(["git", "branch", "--show-current"]) ################## try: origin_branch_exists = not run_git_command(["git", "rev-parse", "--verify", "--quiet", "origin/" + user]) origin_branch_exists = origin_branch_exists != "" except subprocess.CalledProcessError: origin_branch_exists = False try: local_branch_exists = run_git_command(["git", "rev-parse", "--verify", "--quiet", user]) local_branch_exists = local_branch_exists != "" except subprocess.CalledProcessError: local_branch_exists = False if not origin_branch_exists and not local_branch_exists: print(f"git branch {user} does not exist locally or on the remote") # the branch does not exist locally or on the remote so create it here first # checkout new_user run_git_command(["git", "checkout", "new_user"]) # create new branch run_git_command(["git", "checkout", "-b", user]) # creates and checks out new branch # set the upstream to the remote run_git_command(["git", "branch", "--set-upstream-to", "origin/" + user]) # push the branch to the remote run_git_command(["git", "push", "-u", "origin", user]) # fetch and pull it back down for good measure run_git_command(["git", "fetch"]) run_git_command(["git", "pull"]) elif not origin_branch_exists and local_branch_exists: print(f"git branch {user} exists locally but not on the remote") # the branch does not exist on the remote but does locally # set the upstream run_git_command(["git", "branch", "--set-upstream-to", "origin/" + user]) # push the branch to the remote run_git_command(["git", "push", "-u", "origin", user]) # fetch and pull it back down for good measure run_git_command(["git", "fetch"]) run_git_command(["git", "pull"]) elif origin_branch_exists and not local_branch_exists: print(f"git branch {user} exists on the remote but not locally") # the branch does not exist locally but does on the remote # fetch and pull it down run_git_command(["git", "checkout", user]) # pull run_git_command(["git", "pull"]) elif origin_branch_exists and local_branch_exists: print(f"git branch {user} exists locally and on the remote") # the branch exists locally and on the remote # pull it down run_git_command(["git", "checkout", user]) # set the upstream to the remote run_git_command(["git", "branch", "--set-upstream-to", "origin/" + user]) current_branch = run_git_command(["git", "branch", "--show-current"]) # if the current branch is not the user's branch, then switch to it if current_branch != user: print(f"git branch {user} is not the current branch, switching to it") # also check if the branch exists, if not then create it branches = run_git_command(["git", "branch"]) if user not in branches: run_git_command(["git", "branch", user]) # be verbose about what exactly is happening where print(f"git branch {user} does not exist locally, creating it, and pushing it to the remote") run_git_command(["git", "push", "--set-upstream", "origin", user]) # switch to the user's branch run_git_command(["git", "checkout", user]) # pull the latest from the user's branch run_git_command(["git", "pull"]) def git_save_version_1(self, user: str, file_name: str, file_data: str): """ for git save version 1 each user will have their own branch, and the data will be saved to a file in the user's branch version > 1 may include other strategies """ self.ensure_git_state(user) # change to correct directory os.chdir(self.real_git_io_path) # switch to the users branch os.system("git checkout " + user) # save the file data as the file to the disk with open(os.path.join(self.real_git_io_path, file_name), "w", encoding="utf-8") as f: f.write(file_data) # add the file to the git repo os.system("git add " + file_name) # commit the file to the git repo os.system("git commit -m \"saving file " + file_name + "\"") # push the file to the git repo os.system("git push") def git_load_version_1(self, user_name, file_name): """ for git load version 1 each user will have their own branch, and the data will be loaded from a file in the user's branch version > 1 may include other strategies """ self.ensure_git_state(user_name) # change to correct directory os.chdir(self.real_git_io_path) # switch to the users branch os.system("git checkout " + user_name) # pull the latest from the user's branch os.system("git pull") # read the file from the disk with open(os.path.join(self.real_git_io_path, file_name), "r", encoding="utf-8") as f: file_data = f.read() return file_data git_io = GitIO(real_git_url, GDE_PATH) user_data = { "tasha": {"password": "qBizWaxq", "security": None, "client_id": None}, "user1": {"password": "pass1", "security": None, "client_id": None}, } def get_user_by_client_id(client_id): for user in user_data: if user_data[user]["client_id"] == client_id: return user return None async def get_user_if_client_id_logged_in(client_id): try: user = get_user_by_client_id(client_id) except KeyError: user = None return user def get_user_by_security(security): for user in user_data: if user_data[user]["security"] == security: return user return None @PromptServer.instance.routes.post("/gde/logout") async def logout(request): """ This function is called when the user logs out """ print("logout request") # get the user and pass from the json data = await request.json() client_id = data["client_id"] # find the user with the client id user = get_user_by_client_id(client_id) if user is None: # use json to send the data back return web.json_response({"error": "invalid client_id"}, status=401) # clear the security and client id user_data[user]["security"] = None user_data[user]["client_id"] = None # use json to send the data back return web.json_response({"success": True}) @PromptServer.instance.routes.post("/gde/login") async def login(request): """ This function is called when the attemnpts to log in: - validates login - sets up security - user and pass are in the json """ print("login request") # get the user and pass from the json data = await request.json() try: client_id = data["client_id"] has_client_id = True except: # this means the client id is not set yet so return logged_in False # use json to send the data back has_client_id = False return web.json_response({"logged_in": False}) try: user = data["username"] password = data["password"] has_user_and_pass = True except: has_user_and_pass = False # this means the client is just checking to see if it is already logged into the server # so check user = get_user_by_client_id(client_id) if user is None: print("user is none") return web.json_response({"logged_in": False}, status=401) else: # send the client back its user so it can handle api calls correctly print("user recconected " + user) return web.json_response({"logged_in": True, "user": user}, status=200) # validate the user and pass # for now the users and passes are right here in the code if user not in user_data: # use json to send the data back return web.json_response({"error": "invalid user"}, status=401) if password != user_data[user]["password"]: # use json to send the data back return web.json_response({"error": "invalid password"}, status=401) # if the user and pass are valid, then set up security # for now the security is just a random string that will be sent back to the user # in the future this will be a token that will be used to authenticate the user import uuid security = str(uuid.uuid4()) # there is no middleware installed # so we will just store the security in memory # this is not secure, but it is good enough for now # store the security in memory user_data[user]["security"] = security user_data[user]["client_id"] = client_id # return json with the security return web.json_response({"security": security}) def hijack_prompt_server(): """ This function is called by the GDE when it starts up, it hijacks the prompt server for added security. """ # remove the existing prompt route q = [i for i, s in enumerate(PromptServer.instance.routes) if ("/prompt" in str(s))] print(f"found {len(q)} routes to remove: {q}") for adj, i in enumerate(q): route = PromptServer.instance.routes._items.pop(i - adj) method = route.method path = route.path kwargs = route.kwargs if method == "GET": print(f"removing route {method} {path} {kwargs}") get_handler = route.handler @PromptServer.instance.routes.get(path) async def get_prompt(request): print("hijacked prompt get /prompt") print("headers:", request.headers) modified_func_response = await get_handler(request) return modified_func_response elif method == "POST": print(f"removing route {method} {path} {kwargs}") post_handler = route.handler @PromptServer.instance.routes.post(path) async def post_prompt(request: web.Request): # print("hijacked prompt post /prompt") # print("headers:", request.headers) json_data = await request.json() client_id = json_data.get("client_id", None) if client_id is None: print("client_id is None") return web.Response(text="client_id is None", status=400) else: print("client_id:", client_id) # find in users by client_id user = await get_user_if_client_id_logged_in(client_id) if user is None: print("user not found") return web.json_response({"error": "user not found"}, status=401) else: print(f"minimal security check passed for user {user}") modified_func_response: web.Response = await post_handler(request) print(modified_func_response.headers) return modified_func_response # return await post_handler(request) # return post_handler(request) from aiohttp import web @PromptServer.instance.routes.post("/gde/git/user_graphs") async def get_user_graphs(request): """ Returns a list of available user graphs (dropdown options). """ import os import subprocess def run_git_command(command: list, dir_path: str): """Helper function to run a git command in the specified directory""" return subprocess.call(command, cwd=dir_path) data = await request.json() client_id = data.get("client_id", None) user = data.get("user", None) if user not in user_data: user = get_user_by_client_id(client_id) if await get_user_if_client_id_logged_in(client_id) is None: return web.json_response({"error": "user not found"}, status=401) #user = data.get("user", None) print("getting user graphs for user:", user) # Directory where the user graphs are stored user_graphs_dir = git_io.real_git_io_path # Ensure that the git repo is in the correct state git_io.ensure_git_state(user) run_git_command(["git", "pull", "origin", user], user_graphs_dir) user_graph_files = [f for f in os.listdir(user_graphs_dir) if os.path.isfile(os.path.join(user_graphs_dir, f))] options_data = [{"value": f, "label": f} for f in user_graph_files if f != ""] # options_data = [] return web.json_response(options_data) @PromptServer.instance.routes.post("/gde/git/users") async def get_users(request): """ the list of users is just the list of branches in the git repo""" import subprocess print("getting users") # Ensure that the git repo is in the correct state git_io.ensure_git_state("main") # Change to correct directory os.chdir(git_io.real_git_io_path) # Fetch updates from the remote subprocess.call(["git", "fetch", "--prune"]) # List all remote branches remote_branches_raw = subprocess.check_output(["git", "branch", "-r"]).decode("utf-8").split("\n") remote_branches = [b.replace("origin/", "").strip() for b in remote_branches_raw] # List all local branches local_branches_raw = subprocess.check_output(["git", "branch"]).decode("utf-8").split("\n") local_branches = [b.replace("*", "").strip() for b in local_branches_raw] # Identify local branches not present in the remote branches branches_to_delete = [b for b in local_branches if b not in remote_branches] # Delete these local branches for branch in branches_to_delete: subprocess.call(["git", "branch", "-D", branch]) # Now, get the updated list of local branches branches = subprocess.check_output(["git", "branch"]).decode("utf-8").split("\n") branches = [b.replace("*", "").strip() for b in branches] # Convert the list of branches to the desired format options_data = [{"value": b, "label": b} for b in branches if b != ""] return web.json_response(options_data) @PromptServer.instance.routes.post("/gde/git/save") async def save_to_git(request): """ for git save version 1 each user will have their own branch, and the data will be saved to a file in the user's branch version > 1 may include other strategies """ git_save_version = 1 req_json = await request.json() print("saving to git") req_json = await request.json() data = req_json.get("data", None) user_name = req_json.get("user_name", None) file_name = req_json.get("file_name", None) git_io.git_save_version_1(user_name, file_name, data) # Implement your actual Git saving logic here return web.Response(text="Saved to Git successfully.") @PromptServer.instance.routes.post("/gde/git/load") async def load_from_git(request): """use git_io to load the file from git""" print("loading from git") req_json = await request.json() user_name = req_json.get("user_name", None) file_name = req_json.get("file_name", None) # Implement your actual Git loading logic here file_data = git_io.git_load_version_1(user_name, file_name) return web.json_response({"data": file_data}) check_for_js_extension() if __name__ != 'EternalKernelLiteGraphNodes.server_endpoints': hijack_prompt_server()