diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000000..f93b888dd68 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,45 @@ +name: Test WebUI Startup + +on: [push, pull_request] + +jobs: + test-webui: + runs-on: ubuntu-latest + + steps: + - name: Checkout do código + uses: actions/checkout@v3 + + - name: Instalar dependências de sistema + run: | + sudo apt-get update + sudo apt-get install -y python3-venv curl + + - name: Configurar ambiente virtual e dependências Python + run: | + python3 -m venv venv + source venv/bin/activate + pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Iniciar WebUI em background + run: | + source venv/bin/activate + nohup python webui.py > log.txt 2>&1 & + + - name: Esperar servidor iniciar + run: | + sleep 20 + curl -sf http://127.0.0.1:7860 || (echo "Servidor não respondeu" && cat log.txt && exit 1) + + - name: Encerrar servidor (se rodando) + run: | + curl -X POST http://127.0.0.1:7860/sdapi/v1/server-stop || true + + - name: Rodar webui e salvar log + run: | + python webui.py > log.txt 2>&1 + + - name: Mostrar log final do servidor + run: | + cat log.txt || true diff --git a/webui.py b/webui.py index 421e3b8334f..c1b41516808 100644 --- a/webui.py +++ b/webui.py @@ -1,72 +1,96 @@ -from __future__ import annotations - import os import time +from pathlib import Path +from packaging.version import parse +import gradio + +from modules import ( + shared, + ui_tempdir, + startup_timer, + initialize_util, + progress, + ui, + ui_extra_networks, + timer, + initialize, + script_callbacks, + cmd_opts +) + +from api import create_api + + +def limpar_temp_dir(): + if shared.opts.clean_temp_dir_at_start: + ui_tempdir.cleanup_tmpdr() + startup_timer.record("cleanup temp dir") + + +def configurar_ui(): + shared.demo = ui.create_ui() + startup_timer.record("create ui") + if not cmd_opts.no_gradio_queue: + shared.demo.queue(64) + + +def decidir_autolancamento(): + if os.getenv('SD_WEBUI_RESTARTING') == '1': + return False + if shared.opts.auto_launch_browser == "Remote" or cmd_opts.autolaunch: + return True + if shared.opts.auto_launch_browser == "Local": + return not cmd_opts.webui_is_non_local + return False + + +def lancar_interface(auto_launch_browser): + gradio_auth_creds = list(initialize_util.get_gradio_auth_creds()) or None + return shared.demo.launch( + share=cmd_opts.share, + server_name=initialize_util.gradio_server_name(), + server_port=cmd_opts.port, + ssl_keyfile=cmd_opts.tls_keyfile, + ssl_certfile=cmd_opts.tls_certfile, + ssl_verify=cmd_opts.disable_tls_verify, + debug=cmd_opts.gradio_debug, + auth=gradio_auth_creds, + inbrowser=auto_launch_browser, + prevent_thread_lock=True, + allowed_paths=cmd_opts.gradio_allowed_path, + app_kwargs={"docs_url": "/docs", "redoc_url": "/redoc"}, + root_path=f"/{cmd_opts.subpath}" if cmd_opts.subpath else "", + ) -from modules import timer -from modules import initialize_util -from modules import initialize - -startup_timer = timer.startup_timer -startup_timer.record("launcher") - -initialize.imports() - -initialize.check_versions() - - -def create_api(app): - from modules.api.api import Api - from modules.call_queue import queue_lock - - api = Api(app, queue_lock) - return api - - -def api_only(): - from fastapi import FastAPI - from modules.shared_cmd_options import cmd_opts - - initialize.initialize() - app = FastAPI() +def proteger_app(app): + app.user_middleware = [ + x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware' + ] initialize_util.setup_middleware(app) - api = create_api(app) - - from modules import script_callbacks - script_callbacks.before_ui_callback() - script_callbacks.app_started_callback(None, app) - - print(f"Startup time: {startup_timer.summary()}.") - api.launch( - server_name=initialize_util.gradio_server_name(), - port=cmd_opts.port if cmd_opts.port else 7861, - root_path=f"/{cmd_opts.subpath}" if cmd_opts.subpath else "" - ) -def warning_if_invalid_install_dir(): - """ - Shows a warning if the webui is installed under a path that contains a leading dot in any of its parent directories. +def configurar_apis(app): + progress.setup_progress_api(app) + ui.setup_ui_api(app) + if cmd_opts.api: + create_api(app) + ui_extra_networks.add_pages_to_demo(app) - Gradio '/file=' route will block access to files that have a leading dot in the path segments. - We use this route to serve files such as JavaScript and CSS to the webpage, - if those files are blocked, the webpage will not function properly. - See https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13292 - This is a security feature was added to Gradio 3.32.0 and is removed in later versions, - this function replicates Gradio file access blocking logic. +def monitorar_comandos(): + while True: + server_command = shared.state.wait_for_server_command(timeout=5) + if server_command: + if server_command in ("stop", "restart"): + return server_command + else: + print(f"Unknown server command: {server_command}") - This check should be removed when it's no longer applicable. - """ - from packaging.version import parse - from pathlib import Path - import gradio +def warning_if_invalid_install_dir(): if parse('3.32.0') <= parse(gradio.__version__) < parse('4'): - def abspath(path): - """modified from Gradio 3.41.2 gradio.utils.abspath()""" if path.is_absolute(): return path is_symlink = path.is_symlink() or any(parent.is_symlink() for parent in path.parents) @@ -77,81 +101,29 @@ def abspath(path): print(f'''{"!"*25} Warning {"!"*25} WebUI is installed in a directory that has a leading dot (.) in one of its parent directories. This will prevent WebUI from functioning properly. -Please move the installation to a different directory. Current path: "{webui_root}" -For more information see: https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13292 +Please move the installation to a different directory. +More info: https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/13292 {"!"*25} Warning {"!"*25}''') def webui(): - from modules.shared_cmd_options import cmd_opts - - launch_api = cmd_opts.api initialize.initialize() - - from modules import shared, ui_tempdir, script_callbacks, ui, progress, ui_extra_networks - warning_if_invalid_install_dir() - while 1: - if shared.opts.clean_temp_dir_at_start: - ui_tempdir.cleanup_tmpdr() - startup_timer.record("cleanup temp dir") - + while True: + limpar_temp_dir() script_callbacks.before_ui_callback() startup_timer.record("scripts before_ui_callback") - shared.demo = ui.create_ui() - startup_timer.record("create ui") - - if not cmd_opts.no_gradio_queue: - shared.demo.queue(64) - - gradio_auth_creds = list(initialize_util.get_gradio_auth_creds()) or None - - auto_launch_browser = False - if os.getenv('SD_WEBUI_RESTARTING') != '1': - if shared.opts.auto_launch_browser == "Remote" or cmd_opts.autolaunch: - auto_launch_browser = True - elif shared.opts.auto_launch_browser == "Local": - auto_launch_browser = not cmd_opts.webui_is_non_local - - app, local_url, share_url = shared.demo.launch( - share=cmd_opts.share, - server_name=initialize_util.gradio_server_name(), - server_port=cmd_opts.port, - ssl_keyfile=cmd_opts.tls_keyfile, - ssl_certfile=cmd_opts.tls_certfile, - ssl_verify=cmd_opts.disable_tls_verify, - debug=cmd_opts.gradio_debug, - auth=gradio_auth_creds, - inbrowser=auto_launch_browser, - prevent_thread_lock=True, - allowed_paths=cmd_opts.gradio_allowed_path, - app_kwargs={ - "docs_url": "/docs", - "redoc_url": "/redoc", - }, - root_path=f"/{cmd_opts.subpath}" if cmd_opts.subpath else "", - ) + configurar_ui() + auto_launch_browser = decidir_autolancamento() + app, _, _ = lancar_interface(auto_launch_browser) startup_timer.record("gradio launch") - # gradio uses a very open CORS policy via app.user_middleware, which makes it possible for - # an attacker to trick the user into opening a malicious HTML page, which makes a request to the - # running web ui and do whatever the attacker wants, including installing an extension and - # running its code. We disable this here. Suggested by RyotaK. - app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware'] - - initialize_util.setup_middleware(app) - - progress.setup_progress_api(app) - ui.setup_ui_api(app) - - if launch_api: - create_api(app) - - ui_extra_networks.add_pages_to_demo(app) + proteger_app(app) + configurar_apis(app) startup_timer.record("add APIs") @@ -162,26 +134,17 @@ def webui(): print(f"Startup time: {startup_timer.summary()}.") try: - while True: - server_command = shared.state.wait_for_server_command(timeout=5) - if server_command: - if server_command in ("stop", "restart"): - break - else: - print(f"Unknown server command: {server_command}") + server_command = monitorar_comandos() except KeyboardInterrupt: print('Caught KeyboardInterrupt, stopping...') server_command = "stop" if server_command == "stop": print("Stopping server...") - # If we catch a keyboard interrupt, we want to stop the server and exit. shared.demo.close() break - # disable auto launch webui in browser for subsequent UI Reload os.environ.setdefault('SD_WEBUI_RESTARTING', '1') - print('Restarting UI...') shared.demo.close() time.sleep(0.5) @@ -194,9 +157,4 @@ def webui(): if __name__ == "__main__": - from modules.shared_cmd_options import cmd_opts - - if cmd_opts.nowebui: - api_only() - else: - webui() + webui()