Files
anchorhost-backend/server_records.py
T
wfalkowski 6732ee9d47
Build and publish Docker image / build-and-publish (push) Successful in 8s
Fix Kubernetes namespaces for dotted usernames
2026-10-20 19:38:31 +00:00

209 lines
7.9 KiB
Python

"""Server records operations."""
from database_service import database_connection, dict_from_row
from namespace_utils import user_namespace
import json
from runtime_config import (
logger,
)
from typing import Dict, List, Optional, Tuple
from user_service import get_user_by_id
import uuid
__all__ = ['create_server_record', 'delete_server_record', 'get_all_servers', 'get_server_access_and_namespace', 'get_server_by_id', 'get_user_servers', 'update_server_status']
def create_server_record(server_data: Dict) -> Optional[Dict]:
"""Create server record"""
try:
with database_connection() as conn:
cursor = conn.cursor()
is_demo = server_data.get('is_demo', False)
demo_expires_at = server_data.get('demo_expires_at')
cursor.execute('''
INSERT INTO servers (id, name, type, image, version, status, cpu, ram, storage, ip, ports, owner_id, namespace, k8s_name, is_demo, demo_expires_at, java_version, loader, sftp_username, sftp_password, sftp_port)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
server_data['id'],
server_data['name'],
server_data['type'],
server_data['image'],
server_data['version'],
server_data.get('status', 'Creating'),
server_data['cpu'],
server_data['ram'],
server_data['storage'],
server_data['ip'],
json.dumps(server_data['ports']),
server_data['ownerId'], # This should match the key in server_data
server_data.get('namespace', f"user-{server_data['ownerId']}".lower()),
server_data.get('k8s_name', server_data['id']),
is_demo,
demo_expires_at,
server_data.get('java_version'),
server_data.get('loader'),
server_data.get('sftp_username'),
server_data.get('sftp_password'),
server_data.get('sftp_port')
))
conn.commit()
# Return the server with owner_id field
cursor.execute('SELECT * FROM servers WHERE id = ?', (server_data['id'],))
server = dict_from_row(cursor.fetchone())
if server and 'ports' in server:
server['ports'] = json.loads(server['ports'])
return server
except Exception as e:
logger.error(f"Error creating server record: {e}")
return None
def get_server_by_id(server_id: str) -> Optional[Dict]:
"""Get server by ID"""
try:
# Ensure server_id is a string
server_id = str(server_id)
with database_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM servers WHERE id = ?', (server_id,))
server = dict_from_row(cursor.fetchone())
if server and 'ports' in server:
server['ports'] = json.loads(server['ports'])
# Add backward compatibility for ownerId field
if server and 'owner_id' in server:
server['ownerId'] = server['owner_id']
return server
except Exception as e:
return None
def get_server_access_and_namespace(server_id: str, requester_id: str) -> Tuple[str, Dict]:
"""
Check if a user has access to a server and return the namespace and server record.
Access is granted if user is the owner OR an admin.
Returns (namespace, server_record)
"""
server = get_server_by_id(server_id)
if not server:
raise Exception("Server not found")
requester = get_user_by_id(requester_id)
if not requester:
raise Exception("User not found")
# Check access (ensure string comparison for IDs)
# Handle both 'owner_id' and 'ownerId' keys for compatibility
server_owner_id = str(server.get('owner_id') or server.get('ownerId', ''))
req_id = str(requester_id)
is_owner = server_owner_id == req_id
is_admin = requester.get('role') == 'admin'
if not is_owner and not is_admin:
logger.warning(f"Access denied for user {requester_id} to server {server_id}. Owner: {server_owner_id}, Role: {requester.get('role')}")
raise Exception("Access denied")
# Get owner to determine namespace
if is_owner:
owner = requester
else:
owner = get_user_by_id(server_owner_id)
if not owner:
raise Exception("Server owner not found")
namespace = server.get('namespace') or user_namespace(owner['username'])
return namespace, server
def get_user_servers(user_id: str) -> List[Dict]:
"""Get all servers for a user"""
try:
with database_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM servers WHERE owner_id = ? ORDER BY created_at DESC', (user_id,))
servers = [dict_from_row(row) for row in cursor.fetchall()]
for server in servers:
if server and 'ports' in server:
server['ports'] = json.loads(server['ports'])
# Get database configuration for database servers
if server and server['type'] == 'database':
cursor.execute('SELECT * FROM database_configs WHERE server_id = ?', (server['id'],))
db_config = dict_from_row(cursor.fetchone())
if db_config:
server['databaseConfig'] = db_config
return servers
except Exception as e:
return []
def get_all_servers() -> List[Dict]:
"""Get all servers (admin only)"""
try:
with database_connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM servers ORDER BY created_at DESC')
servers = [dict_from_row(row) for row in cursor.fetchall()]
for server in servers:
if server and 'ports' in server:
try:
if isinstance(server['ports'], str):
server['ports'] = json.loads(server['ports'])
except:
server['ports'] = []
# Get database configuration for database servers
if server and server['type'] == 'database':
cursor.execute('SELECT * FROM database_configs WHERE server_id = ?', (server['id'],))
db_config = dict_from_row(cursor.fetchone())
if db_config:
server['databaseConfig'] = db_config
return servers
except Exception as e:
logger.error(f"Error getting all servers: {e}")
return []
def update_server_status(server_id: str, status: str, message: str = "") -> bool:
"""Update server status"""
try:
with database_connection() as conn:
cursor = conn.cursor()
cursor.execute(
'UPDATE servers SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
(status, server_id)
)
status_id = f"status-{str(uuid.uuid4())[:8]}"
cursor.execute('''
INSERT INTO server_status_history (id, server_id, status, message)
VALUES (?, ?, ?, ?)
''', (status_id, server_id, status, message))
conn.commit()
return cursor.rowcount > 0
except Exception as e:
return False
def delete_server_record(server_id: str) -> bool:
"""Delete server record"""
try:
with database_connection() as conn:
cursor = conn.cursor()
# Delete database configuration first
cursor.execute('DELETE FROM database_configs WHERE server_id = ?', (server_id,))
# Then delete server
cursor.execute('DELETE FROM servers WHERE id = ?', (server_id,))
conn.commit()
return cursor.rowcount > 0
except Exception as e:
return False