diff --git a/apphub/src/api/v1/routers/proxy.py b/apphub/src/api/v1/routers/proxy.py index 7ee8b595..4379b073 100755 --- a/apphub/src/api/v1/routers/proxy.py +++ b/apphub/src/api/v1/routers/proxy.py @@ -1,6 +1,6 @@ from typing import List, Optional -from fastapi import APIRouter, Query,Path +from fastapi import APIRouter, Query,Path,Request,Depends from fastapi.params import Body from src.schemas.domainNames import DomainNames from src.schemas.errorResponse import ErrorResponse @@ -59,22 +59,22 @@ def update_proxys( ): return AppManger().update_proxy_by_app(proxy_id,domain_names.domain_names,endpointId) -@router.delete( - "/proxys/app/{app_id}", - summary="Delete Proxys", - description="Delete proxys by app", - status_code=204, - responses={ - 204: {"description": "Delete Proxys Success"}, - 400: {"model": ErrorResponse}, - 500: {"model": ErrorResponse}, - } - ) -def delete_proxys_by_app( - app_id: str = Path(..., description="App ID to create proxys from"), - endpointId: int = Query(None, description="Endpoint ID to create proxys from. If not set, create proxys from the local endpoint"), -): - AppManger().remove_proxy_by_app(app_id,endpointId) +# @router.delete( +# "/proxys/app/{app_id}", +# summary="Delete Proxys", +# description="Delete proxys by app", +# status_code=204, +# responses={ +# 204: {"description": "Delete Proxys Success"}, +# 400: {"model": ErrorResponse}, +# 500: {"model": ErrorResponse}, +# } +# ) +# def delete_proxys_by_app( +# app_id: str = Path(..., description="App ID to create proxys from"), +# endpointId: int = Query(None, description="Endpoint ID to create proxys from. If not set, create proxys from the local endpoint"), +# ): +# AppManger().remove_proxy_by_app(app_id,endpointId) @router.delete( "/proxys/{proxy_id}", @@ -88,6 +88,10 @@ def delete_proxys_by_app( } ) def delete_proxys_by_id( + request: Request, proxy_id: int = Path(..., description="Proxy ID to delete proxys from") + ): - AppManger().remove_proxy_by_id(proxy_id) \ No newline at end of file + client_host = request.headers.get("Host") + # client_host = request.client.host + AppManger().remove_proxy_by_id(proxy_id,client_host) \ No newline at end of file diff --git a/apphub/src/services/app_manager.py b/apphub/src/services/app_manager.py index 3550162f..dce94c52 100755 --- a/apphub/src/services/app_manager.py +++ b/apphub/src/services/app_manager.py @@ -1,3 +1,4 @@ +import base64 import json import os import shutil @@ -245,7 +246,7 @@ class AppManger: main_container_info = portainerManager.get_container_by_id(endpointId, main_container_id) # Get the env from main_container_info app_env = main_container_info.get("Config", {}).get("Env", []) - + # Get info from app_env app_name = None app_dist = None @@ -333,7 +334,7 @@ class AppManger: endpointId = portainerManager.get_local_endpoint_id() # generate app_id - app_id = app_id + "_" + PasswordGenerator.generate_weak_password(6) + app_id = app_id + "_" + PasswordGenerator.generate_random_string(5) # add app to appInstalling app_uuid = start_app_installation(app_id, app_name) @@ -389,7 +390,7 @@ class AppManger: envHelper.set_value("W9_ID", app_id) envHelper.set_value("W9_DIST", "community") envHelper.set_value("W9_VERSION", app_version) - envHelper.set_value("POWER_PASSWORD", PasswordGenerator.generate_strong_password()) + envHelper.set_value("W9_POWER_PASSWORD", PasswordGenerator.generate_strong_password()) # Get "W9_URL" from env file (validate the app is web app) is_web_app = envHelper.get_value("W9_URL") @@ -921,6 +922,30 @@ class AppManger: forward_port = https_port # Create proxy if forward_port: + w9_url = stack_env.get("W9_URL",None) + w9_url_replace = stack_env.get("W9_URL_REPLACE",None) + + if w9_url and w9_url_replace: + # Get the all proxys by app_id + all_domain_names = proxyManager.get_proxy_host_by_app(app_id) + # if all_domain_names is empty,create proxy + if not all_domain_names: + # update the env file + self._update_gitea_env_file(app_id,w9_url,domain_names[0]) + # redeploy app + self.redeploy_app(app_id,False) + else: + combined_domain_names = [] + for item in all_domain_names: + combined_domain_names.extend(item['domain_names']) + combined_domain_names.extend(domain_names) + + if w9_url not in combined_domain_names: + # update the env file + self._update_gitea_env_file(app_id,w9_url,domain_names[0]) + # redeploy app + self.redeploy_app(app_id,False) + # Get the forward scheme form env file: http or https proxy_host = proxyManager.create_proxy_by_app(domain_names,app_id,forward_port,forward_scheme=forward_scheme) if proxy_host: @@ -974,7 +999,7 @@ class AppManger: proxyManager.remove_proxy_host_by_app(app_id) logger.access(f"Successfully removed all domains for app: [{app_id}]") - def remove_proxy_by_id(self,proxy_id:int): + def remove_proxy_by_id(self,proxy_id:int,client_host:str): """ Remove proxy by proxy_id @@ -982,29 +1007,59 @@ class AppManger: proxy_id (int): The proxy id. """ # Check the proxy id is exists - host = ProxyManager().get_proxy_host_by_id(proxy_id) - if host is None: - raise CustomException( - status_code=400, - message="Invalid Request", - details=f"Proxy ID:{proxy_id} Not Found" - ) - # # Get the app_id by proxy_id - # app_id = host.get("forward_host",None) - # if app_id: - # # Get the app_info by app_id - # app_info = self.get_app_by_id(app_id) - # if app_info: - # # Get the w9_url and w9_url_replace - # w9_url = app_info.get("w9_url",None) - # w9_url_replace = app_info.get("w9_url_replace",None) - - # if w9_url_replace: - # domain_names = host.get("domain_names",None) + try: + proxyManager = ProxyManager() + host = proxyManager.get_proxy_host_by_id(proxy_id) + if host is None: + raise CustomException( + status_code=400, + message="Invalid Request", + details=f"Proxy ID:{proxy_id} Not Found" + ) + # Get the app_id by proxy_id + app_id = host.get("forward_host",None) + if app_id: + # Get the app_info by app_id + app_info = self.get_app_by_id(app_id) + if app_info: + # Get the w9_url and w9_url_replace + w9_url_replace = next((element.get("w9_url_replace") for element in app_info.domain_names if element.get("id") == proxy_id), None) + w9_url = next((element.get("w9_url") for element in app_info.domain_names if element.get("id") == proxy_id), None) + + # validate w9_url_replace is true + if w9_url_replace: + domain_names = host.get("domain_names",None) + if domain_names: + # Get the all proxys by app_id + app_proxys = self.get_proxys_by_app(app_id) + # if w9_url is in domain_names: + if w9_url in domain_names: + new_w9_url = None + if len(app_proxys) == 1 and app_proxys[0].get("id") == proxy_id: + new_w9_url = client_host + elif len(app_proxys) > 1: + # Get the first proxy_host + proxy_host = next((proxy for proxy in app_proxys if proxy.get("id") != proxy_id), None) + if proxy_host: + # Get the domain_names + domain_names = proxy_host.get("domain_names",None) + if domain_names: + # Get the first domain_name + new_w9_url = domain_names[0] + + # update the env file + self._update_gitea_env_file(app_id,w9_url,new_w9_url) - # Remove proxy - ProxyManager().remove_proxy_host_by_id(proxy_id) - logger.access(f"Successfully removed domains:{host['domain_names']} for app: [{host['forward_host']}]") + # redeploy app + self.redeploy_app(app_id,False) + # Remove proxy + proxyManager.remove_proxy_host_by_id(proxy_id) + logger.access(f"Successfully removed domains:{host['domain_names']} for app: [{app_id}]") + except CustomException as e: + raise e + except Exception as e: + logger.error(f"Remove proxy error:{e}") + raise CustomException() def update_proxy_by_app(self,proxy_id:str,domain_names:list[str],endpointId:int = None): """ @@ -1032,8 +1087,27 @@ class AppManger: message="Invalid Request", details=f"Proxy ID:{proxy_id} Not Found" ) - - # check_domain_names(domain_names) + + # Get the app_id by proxy_id + app_id = host.get("forward_host",None) + old_domain_names = host.get("domain_names",None) + logger.access(f"old_domain_names:{old_domain_names}") + if app_id: + # Get the app_info by app_id + app_info = self.get_app_by_id(app_id) + if app_info: + # Get the w9_url and w9_url_replace + w9_url_replace = next((element.get("w9_url_replace") for element in app_info.domain_names if element.get("id") == proxy_id), None) + w9_url = next((element.get("w9_url") for element in app_info.domain_names if element.get("id") == proxy_id), None) + + # validate w9_url_replace is true + if w9_url_replace and w9_url: + if w9_url in old_domain_names: + if w9_url not in domain_names: + # update the env file + self._update_gitea_env_file(app_id,w9_url,domain_names[0]) + # redeploy app + self.redeploy_app(app_id,False) # Update proxy result = proxyManager.update_proxy_by_app(proxy_id,domain_names) @@ -1064,4 +1138,37 @@ class AppManger: logger.error(f"Init local repo and push to remote repo error:{e}") raise CustomException() - \ No newline at end of file + def _update_gitea_env_file(self,app_id:str,key:str,value:str): + """ + Update the env file w9_url + + Args: + app_id (str): The app id. + domain_name (str): The domain name. + """ + try: + giteaManager = GiteaManager() + # Get the env file from git repo + git_env_file = giteaManager.get_file_content_from_repo(app_id,".env") + # Get the env file sha + git_env_file_sha = git_env_file.get("sha",None) + # Get the env file content + git_env_file_content = git_env_file.get("content",None) + if git_env_file_sha and git_env_file_content: + # Get the env file content + env_file_content = base64.b64decode(git_env_file_content).decode("utf-8") + # Modify the env file content + env_file_content = env_file_content.replace(key,value) + # base64 encode for env_file_content + env_file_content = base64.b64encode(env_file_content.encode("utf-8")).decode("utf-8") + # Update the env file to git repo + giteaManager.update_file_in_repo(app_id,".env",env_file_content,git_env_file_sha) + logger.access(f"Update the git repo env file for app: [{app_id}]") + else: + logger.error(f"Get the git repo env file error") + raise CustomException() + except CustomException as e: + raise e + except Exception as e: + logger.error(f"Update the git repo env file error:{e}") + raise CustomException() \ No newline at end of file diff --git a/apphub/src/services/gitea_manager.py b/apphub/src/services/gitea_manager.py index 6f24d8dd..56b951e3 100755 --- a/apphub/src/services/gitea_manager.py +++ b/apphub/src/services/gitea_manager.py @@ -116,7 +116,7 @@ class GiteaManager: sha (str): File sha """ response = self.gitea.update_file_content_in_repo(repo_name, file_path, content, sha) - if response.status_code != 201: + if response.status_code != 200: logger.error(f"Update file:{file_path} content in repo:{repo_name} error:{response.status_code}:{response.text}") raise CustomException() diff --git a/apphub/src/utils/password_generator.py b/apphub/src/utils/password_generator.py index 24132ccf..4b060db2 100755 --- a/apphub/src/utils/password_generator.py +++ b/apphub/src/utils/password_generator.py @@ -33,8 +33,9 @@ class PasswordGenerator: return password + @staticmethod - def generate_weak_password(length:int=8): + def generate_random_string(length:int=8): """ Generate a weak password. @@ -44,5 +45,5 @@ class PasswordGenerator: Returns: str: A weak password. """ - return ''.join(random.choice(string.ascii_lowercase) for _ in range(length)) - + characters = string.ascii_lowercase + string.digits + return ''.join(random.choice(characters) for _ in range(length)) \ No newline at end of file