diff --git a/README.md b/README.md index b0f9bbe..caf7e3f 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,4 @@ An example can be found in `./script_examples/websockets_api_example.py`. Feel free to modify `login.html` if you want to personalize your login page. ## HTTPS? -Now ComfyUI officially supports HTTPS. Please check [How To Use TLS/SSL](https://github.com/comfyanonymous/ComfyUI?tab=readme-ov-file#how-to-use-tlsssl) section. +Now ComfyUI officially supports self-signed SSL certificates (though Chrome might display a warning for them). Please check [How To Use TLS/SSL](https://github.com/comfyanonymous/ComfyUI?tab=readme-ov-file#how-to-use-tlsssl) section. diff --git a/__init__.py b/__init__.py index 9b291fd..39f5809 100644 --- a/__init__.py +++ b/__init__.py @@ -3,6 +3,7 @@ import aiohttp from aiohttp_session import setup, get_session from aiohttp_session.cookie_storage import EncryptedCookieStorage from aiohttp import web +from jinja2 import Environment, FileSystemLoader, select_autoescape import base64 import os import folder_paths @@ -14,9 +15,30 @@ node_dir = os.path.dirname(__file__) comfy_dir = os.path.dirname(folder_paths.__file__) password_path = os.path.join(comfy_dir, "PASSWORD") secret_key_path = os.path.join(node_dir,'.secret-key.txt') +login_html_path = os.path.join(node_dir, "login.html") KEY_AGE_LIMIT = timedelta(days=30) # Key expiration period TOKEN = "" +# Global cache dictionary +user_cache = {} + +def get_user_data(): + if 'username' in user_cache: + return user_cache['username'], user_cache['password'] + else: + if os.path.exists(password_path): + with open(password_path, "rb") as f: + stored_data = f.read().split(b'\n') + password = stored_data[0] + user_cache['password'] = password + username = 'User' + if len(stored_data) > 1: + # Assuming username is stored on the second line, password on the first + username = stored_data[1].decode('utf-8') + user_cache['username'] = username + return username, password + return None, None + def generate_key(): return base64.urlsafe_b64encode(os.urandom(32)).decode('utf-8') @@ -53,41 +75,55 @@ setup(app, EncryptedCookieStorage(secret_key)) @routes.get("/login") async def get_root(request): session = await get_session(request) - if ('logged_in' in session and session['logged_in']): + feedback = request.query.get('feedback', '') # Get feedback from query parameters + if 'logged_in' in session and session['logged_in']: raise web.HTTPFound('/') else: - return web.FileResponse(os.path.join(node_dir, "login.html")) + env = Environment( + loader=FileSystemLoader(node_dir), + autoescape=select_autoescape(['html', 'xml']) + ) + template = env.get_template('login.html') + first_time = not os.path.exists(password_path) + username = '' + if not first_time: + username, _ = get_user_data() + return web.Response(text=template.render(first_time=first_time, username=username, feedback=feedback), content_type='text/html') -# Add a route for "/login" that handles the form submission @routes.post("/login") async def login_handler(request): - data = await request.post() # Get the data from the form - password = data.get('password').encode('utf-8') + data = await request.post() + username_input = data.get('username') + password_input = data.get('password').encode('utf-8') + feedback = '' if os.path.exists(password_path): - with open(password_path, "rb") as f: - hashed_password = f.read() - if bcrypt.checkpw(password, hashed_password): + # Existing user login attempt + username_cached, password_cached = get_user_data() + if bcrypt.checkpw(password_input, password_cached): session = await get_session(request) session['logged_in'] = True - response = web.HTTPFound('/') # Redirect to the main page if the password is correct - return response + session['username'] = username_cached + return web.HTTPFound('/') else: - return web.HTTPFound('/login?feedback=Wrong password') + feedback = 'Wrong password' else: + # New user setup salt = bcrypt.gensalt() - hashed_password = bcrypt.hashpw(password, salt) - # Write the hashed password to a file + hashed_password = bcrypt.hashpw(password_input, salt) with open(password_path, "wb") as file: - file.write(hashed_password) + file.write(hashed_password + b'\n' + username_input.encode('utf-8')) + user_cache['username'] = username_input + user_cache['password'] = hashed_password session = await get_session(request) session['logged_in'] = True - response = web.HTTPFound('/') # Redirect to the main page if the password is correct - return response + session['username'] = username_input + return web.HTTPFound('/login?feedback=' + feedback) @routes.get("/logout") async def get_root(request): session = await get_session(request) session['logged_in'] = False + session.pop('username', None) # Clear the username response = web.HTTPFound('/login') # Redirect to the main page if the password is correct return response @@ -95,7 +131,7 @@ def load_token(): global TOKEN try: with open(password_path, "r", encoding="utf-8") as f: - TOKEN = f.read() + TOKEN = f.readline().strip() # Read only the first line and strip any newline characters logging.info(f"For direct API calls, use token={TOKEN}") except FileNotFoundError as e: logging.error("Please set up your password before use. The token will be a hashed string derived from your password.") diff --git a/login.html b/login.html index 113b786..2eda64e 100644 --- a/login.html +++ b/login.html @@ -10,35 +10,30 @@
{{ feedback }}
+ {% endif %} + {% if first_time %} +Welcome!
+For your first login, please enter a new username and password.
+Your password will be securely saved for future access.
+On subsequent visits, you can use this password to log in.
+ {% endif %} +