From e744afbe26c66f5878ee2e630db6789e0f55a556 Mon Sep 17 00:00:00 2001 From: Matt Hills Date: Thu, 19 Jun 2025 16:03:50 -0400 Subject: [PATCH] adding server settings --- src/app.py | 23 ++++++++-- src/database.py | 70 +++++++++++++++++++++++++++-- src/templates/settings.html | 52 +++++++++++++++++++++ src/templates/tenant_selection.html | 2 + 4 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 src/templates/settings.html diff --git a/src/app.py b/src/app.py index 1ab0ce9..f94377d 100644 --- a/src/app.py +++ b/src/app.py @@ -28,7 +28,8 @@ def load_products(tenant_id=None): def index(): # Show a tenant selection page or redirect to default tenants = database.get_all_tenants() - return render_template('tenant_selection.html', tenants=tenants) + server_url = database.get_server_url() + return render_template('tenant_selection.html', tenants=tenants, server_url=server_url) @app.route('/tenant//delete', methods=['POST']) def delete_tenant(tenant_id): @@ -37,6 +38,22 @@ def delete_tenant(tenant_id): flash(f'Tenant "{tenant_id}" has been deleted successfully.', 'success') return redirect('/') +@app.route('/settings', methods=['GET', 'POST']) +def settings(): + if request.method == 'POST': + server_url = request.form.get('server_url', '').strip() + if server_url: + # Remove trailing slash for consistency + server_url = server_url.rstrip('/') + database.set_setting('server_url', server_url) + flash('Server settings updated successfully.', 'success') + else: + flash('Please provide a valid server URL.', 'error') + return redirect('/settings') + + server_url = database.get_server_url() + return render_template('settings.html', server_url=server_url) + @app.route('//') def tenant_index(tenant_id): # Auto-create tenant if it doesn't exist @@ -56,8 +73,8 @@ def check_basic_auth(auth_header, tenant_id): credentials = base64.b64decode(auth_header[6:]).decode('utf-8') username, password = credentials.split(':', 1) - # Get tenant credentials from database - tenant = database.get_or_create_tenant(tenant_id) + # Get tenant credentials from database (without creating) + tenant = database.get_tenant(tenant_id) if tenant and username == tenant['username'] and password == tenant['password']: return True except Exception: diff --git a/src/database.py b/src/database.py index d8692ec..836f9a6 100644 --- a/src/database.py +++ b/src/database.py @@ -107,6 +107,16 @@ def init_database(): ) ''') + # Create settings table for server configuration + cursor.execute(''' + CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + conn.commit() def migrate_from_json(): @@ -342,10 +352,34 @@ def get_product_image(product_id: str, tenant_id: str) -> Optional[tuple[bytes, return row['image_data'], row['image_mime_type'] return None +def get_tenant(tenant_id: str) -> Optional[Dict[str, Any]]: + """Get a tenant without creating it""" + # Normalize tenant_id to lowercase + tenant_id = tenant_id.lower() + + with get_db() as conn: + cursor = conn.cursor() + + # Check if tenant exists + cursor.execute('SELECT id, name, username, password FROM tenants WHERE id = ?', (tenant_id,)) + row = cursor.fetchone() + + if row: + return { + 'id': row['id'], + 'name': row['name'], + 'username': row['username'], + 'password': row['password'] + } + return None + def get_or_create_tenant(tenant_id: str, username: str = None, password: str = None) -> Optional[Dict[str, Any]]: """Get or create a tenant""" + # Normalize tenant_id to lowercase + tenant_id = tenant_id.lower() + # Check if tenant_id is reserved - if tenant_id.lower() in RESERVED_TENANT_IDS: + if tenant_id in RESERVED_TENANT_IDS: return None with get_db() as conn: @@ -366,10 +400,12 @@ def get_or_create_tenant(tenant_id: str, username: str = None, password: str = N # Create new tenant with default credentials default_username = username or 'admin' default_password = password or 'admin' + # Preserve original casing for display name + display_name = tenant_id.replace('-', ' ').replace('_', ' ').title() cursor.execute(''' INSERT INTO tenants (id, name, username, password) VALUES (?, ?, ?, ?) - ''', (tenant_id, tenant_id.title(), default_username, default_password)) + ''', (tenant_id, display_name, default_username, default_password)) conn.commit() # Initialize default AR fields for the new tenant @@ -377,7 +413,7 @@ def get_or_create_tenant(tenant_id: str, username: str = None, password: str = N return { 'id': tenant_id, - 'name': tenant_id.title(), + 'name': display_name, 'username': default_username, 'password': default_password } @@ -554,4 +590,30 @@ def get_product_image_by_field(product_id: str, tenant_id: str, field_name: str) row = cursor.fetchone() if row and row['image_data']: return row['image_data'], row['image_mime_type'] - return None \ No newline at end of file + return None + +def get_setting(key: str, default_value: str = None) -> Optional[str]: + """Get a setting value by key""" + with get_db() as conn: + cursor = conn.cursor() + cursor.execute('SELECT value FROM settings WHERE key = ?', (key,)) + row = cursor.fetchone() + + if row: + return row['value'] + return default_value + +def set_setting(key: str, value: str): + """Set a setting value""" + with get_db() as conn: + cursor = conn.cursor() + cursor.execute(''' + INSERT INTO settings (key, value) + VALUES (?, ?) + ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = CURRENT_TIMESTAMP + ''', (key, value, value)) + conn.commit() + +def get_server_url() -> str: + """Get the configured server URL or return a default""" + return get_setting('server_url', 'http://localhost:5000') \ No newline at end of file diff --git a/src/templates/settings.html b/src/templates/settings.html new file mode 100644 index 0000000..0cc6697 --- /dev/null +++ b/src/templates/settings.html @@ -0,0 +1,52 @@ + + + + + + Server Settings - KCAP Demo Server + + + +
+

Server Settings

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + +
+
+
+
+

Server Configuration

+
+
+
+
+ + + + This URL will be used for generating Knox Capture AR Template URLs. + Include the protocol (http:// or https://) and port if needed. + +
+ + Back to Home +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/src/templates/tenant_selection.html b/src/templates/tenant_selection.html index 427672a..4dd29ed 100644 --- a/src/templates/tenant_selection.html +++ b/src/templates/tenant_selection.html @@ -38,6 +38,7 @@ {{ tenant.created_at }}

Username: {{ tenant.username }}

+

Knox Capture AR Template URL: {{ server_url }}/{{ tenant.id }}/

ID: {{ tenant.id }}
@@ -73,6 +74,7 @@ Note: When you access a tenant URL directly (e.g., /my-tenant/), it will be automatically created with default credentials (admin/admin).

+ Server Settings