mirror of
https://github.com/mattintech/simplefileupload-server.git
synced 2026-07-11 17:31:53 +00:00
Move client key to database with admin UI for key management and QR code generation
- Store client key in SQLite database instead of environment variable - Add database migration from CLIENT_KEY environment variable to preserve existing keys - Add admin UI with tabbed interface (Configuration and QR Code tabs) - Implement QR code generation containing server config (address, port, key) for Android app - Add functionality to regenerate client key with warning dialog - Add buttons to download QR code as PNG and copy QR image to clipboard - Add manual input fields for server address and port configuration - Update requirements.txt with pyotp and qrcode dependencies
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from .file_metadata import FileMetadata
|
||||
|
||||
# Database initialization
|
||||
from .database import init_db, get_db_connection, migrate_from_json
|
||||
from .database import init_db, get_db_connection, migrate_from_json, migrate_client_key_to_db
|
||||
|
||||
# Admin model functions
|
||||
from .admin_model import (
|
||||
@@ -33,3 +33,13 @@ from .chunk_model import (
|
||||
cleanup_abandoned,
|
||||
update_received_chunks
|
||||
)
|
||||
|
||||
# Config model functions
|
||||
from .config_model import (
|
||||
get_config,
|
||||
get_client_key,
|
||||
init_config,
|
||||
update_server_address,
|
||||
update_server_port,
|
||||
regenerate_client_key
|
||||
)
|
||||
|
||||
157
src/models/config_model.py
Normal file
157
src/models/config_model.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import sqlite3
|
||||
import secrets
|
||||
from datetime import datetime
|
||||
from models.database import get_db
|
||||
import utils.mLogger as log
|
||||
|
||||
|
||||
def generate_client_key():
|
||||
"""Generate a random 64-character hex string (256 bits)"""
|
||||
return secrets.token_hex(32) # 32 bytes = 64 hex chars
|
||||
|
||||
|
||||
def get_config():
|
||||
"""Get server configuration (singleton)
|
||||
|
||||
Returns:
|
||||
dict: Configuration with client_key, server_address, server_port, timestamps
|
||||
None: If config doesn't exist or error occurred
|
||||
"""
|
||||
try:
|
||||
with get_db() as conn:
|
||||
cursor = conn.cursor()
|
||||
row = cursor.execute("""
|
||||
SELECT client_key, server_address, server_port, created_at, updated_at
|
||||
FROM server_config
|
||||
WHERE id = 1
|
||||
""").fetchone()
|
||||
|
||||
if row:
|
||||
return dict(row)
|
||||
return None
|
||||
except sqlite3.Error as e:
|
||||
log.e(f"Error getting config: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_client_key():
|
||||
"""Get only the client key for authentication
|
||||
|
||||
Returns:
|
||||
str: The client key
|
||||
None: If config doesn't exist or error occurred
|
||||
"""
|
||||
config = get_config()
|
||||
return config['client_key'] if config else None
|
||||
|
||||
|
||||
def init_config(client_key=None):
|
||||
"""Initialize server config with provided or generated key
|
||||
|
||||
Args:
|
||||
client_key (str, optional): Client key to use. If None, generates new key
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
with get_db() as conn:
|
||||
# Check if config already exists
|
||||
existing = conn.execute("SELECT COUNT(*) FROM server_config WHERE id = 1").fetchone()[0]
|
||||
|
||||
if existing > 0:
|
||||
log.i("Server config already exists, skipping initialization")
|
||||
return True
|
||||
|
||||
# Generate key if not provided
|
||||
if not client_key:
|
||||
client_key = generate_client_key()
|
||||
log.i("Generated new client key")
|
||||
else:
|
||||
log.i("Using provided client key from environment")
|
||||
|
||||
# Insert initial config
|
||||
now = datetime.now().isoformat()
|
||||
|
||||
conn.execute("""
|
||||
INSERT INTO server_config (id, client_key, created_at, updated_at)
|
||||
VALUES (1, ?, ?, ?)
|
||||
""", (client_key, now, now))
|
||||
|
||||
log.i("Server config initialized successfully")
|
||||
return True
|
||||
except sqlite3.Error as e:
|
||||
log.e(f"Error initializing config: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def update_server_address(address):
|
||||
"""Update server address
|
||||
|
||||
Args:
|
||||
address (str): Server address (IP or hostname)
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
with get_db() as conn:
|
||||
conn.execute("""
|
||||
UPDATE server_config
|
||||
SET server_address = ?, updated_at = ?
|
||||
WHERE id = 1
|
||||
""", (address, datetime.now().isoformat()))
|
||||
|
||||
log.i(f"Updated server address: {address}")
|
||||
return True
|
||||
except sqlite3.Error as e:
|
||||
log.e(f"Error updating server address: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def update_server_port(port):
|
||||
"""Update server port
|
||||
|
||||
Args:
|
||||
port (int): Server port number
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
with get_db() as conn:
|
||||
conn.execute("""
|
||||
UPDATE server_config
|
||||
SET server_port = ?, updated_at = ?
|
||||
WHERE id = 1
|
||||
""", (port, datetime.now().isoformat()))
|
||||
|
||||
log.i(f"Updated server port: {port}")
|
||||
return True
|
||||
except sqlite3.Error as e:
|
||||
log.e(f"Error updating server port: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def regenerate_client_key():
|
||||
"""Generate and set a new client key
|
||||
|
||||
Returns:
|
||||
str: The new client key
|
||||
None: If error occurred
|
||||
"""
|
||||
try:
|
||||
new_key = generate_client_key()
|
||||
|
||||
with get_db() as conn:
|
||||
conn.execute("""
|
||||
UPDATE server_config
|
||||
SET client_key = ?, updated_at = ?
|
||||
WHERE id = 1
|
||||
""", (new_key, datetime.now().isoformat()))
|
||||
|
||||
log.w("Client key regenerated - all clients must update!")
|
||||
return new_key
|
||||
except sqlite3.Error as e:
|
||||
log.e(f"Error regenerating client key: {e}")
|
||||
return None
|
||||
@@ -110,6 +110,19 @@ def init_db():
|
||||
CREATE INDEX IF NOT EXISTS idx_chunk_sessions_created_at ON chunk_sessions(created_at)
|
||||
""")
|
||||
|
||||
# Server configuration table (singleton)
|
||||
conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS server_config (
|
||||
id INTEGER PRIMARY KEY CHECK(id = 1),
|
||||
client_key TEXT NOT NULL,
|
||||
server_address TEXT,
|
||||
server_port INTEGER,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
CONSTRAINT client_key_length CHECK(length(client_key) = 64)
|
||||
)
|
||||
""")
|
||||
|
||||
log.i("Database initialized successfully")
|
||||
|
||||
def migrate_from_json():
|
||||
@@ -224,3 +237,27 @@ def migrate_from_json():
|
||||
log.i("Migration completed successfully")
|
||||
else:
|
||||
log.i("No migration performed")
|
||||
|
||||
|
||||
def migrate_client_key_to_db():
|
||||
"""Migrate CLIENT_KEY from environment to database (one-time)"""
|
||||
from models.config_model import init_config
|
||||
|
||||
# Only run if config doesn't exist
|
||||
with get_db() as conn:
|
||||
existing = conn.execute("SELECT COUNT(*) FROM server_config WHERE id = 1").fetchone()[0]
|
||||
|
||||
if existing > 0:
|
||||
log.i("Server config already exists in database, skipping migration")
|
||||
return
|
||||
|
||||
# Get CLIENT_KEY from environment if set
|
||||
env_key = os.environ.get('CLIENT_KEY')
|
||||
|
||||
# Initialize config (will generate new key if env_key is None or default)
|
||||
if env_key and env_key != 'default-secret-key':
|
||||
log.i("Migrating CLIENT_KEY from environment to database")
|
||||
init_config(client_key=env_key)
|
||||
else:
|
||||
log.i("No valid CLIENT_KEY in environment, generating new key")
|
||||
init_config()
|
||||
|
||||
Reference in New Issue
Block a user