adding server settings
This commit is contained in:
23
src/app.py
23
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/<tenant:tenant_id>/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('/<tenant:tenant_id>/')
|
||||
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:
|
||||
|
||||
@@ -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
|
||||
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')
|
||||
52
src/templates/settings.html
Normal file
52
src/templates/settings.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Server Settings - KCAP Demo Server</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1 class="text-center mb-4">Server Settings</h1>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'success' if category == 'success' else 'danger' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>Server Configuration</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="server_url" class="form-label">Server URL</label>
|
||||
<input type="url" class="form-control" id="server_url" name="server_url"
|
||||
value="{{ server_url }}" placeholder="http://localhost:5000" required>
|
||||
<small class="form-text text-muted">
|
||||
This URL will be used for generating Knox Capture AR Template URLs.
|
||||
Include the protocol (http:// or https://) and port if needed.
|
||||
</small>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save Settings</button>
|
||||
<a href="/" class="btn btn-secondary">Back to Home</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -38,6 +38,7 @@
|
||||
<small>{{ tenant.created_at }}</small>
|
||||
</div>
|
||||
<p class="mb-1">Username: {{ tenant.username }}</p>
|
||||
<p class="mb-1"><strong>Knox Capture AR Template URL:</strong> <code>{{ server_url }}/{{ tenant.id }}/</code></p>
|
||||
<small>ID: {{ tenant.id }}</small>
|
||||
</a>
|
||||
<form method="POST" action="/tenant/{{ tenant.id }}/delete" class="ms-3" onsubmit="return confirmDelete('{{ tenant.name }}')">
|
||||
@@ -73,6 +74,7 @@
|
||||
<strong>Note:</strong> When you access a tenant URL directly (e.g., /my-tenant/),
|
||||
it will be automatically created with default credentials (admin/admin).
|
||||
</p>
|
||||
<a href="/settings" class="btn btn-secondary mt-3">Server Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user