feat(hermes-web-cli): refactor user context, settings, services and sessions management
- Remove deprecated UNKNOWN.egg-info and user_context.py - Refactor crud_ops, db_tables, and init modules - Update settings UI and save handlers (appearance, general, security) - Update services list, remove, and test DSPY files - Update sessions list DSPY file - Add multi-user test script - Update pyproject.toml dependencies
This commit is contained in:
parent
58dfbb0108
commit
d423a03a6d
@ -1,10 +0,0 @@
|
|||||||
Metadata-Version: 2.1
|
|
||||||
Name: UNKNOWN
|
|
||||||
Version: 0.0.0
|
|
||||||
Summary: UNKNOWN
|
|
||||||
Home-page: UNKNOWN
|
|
||||||
License: UNKNOWN
|
|
||||||
Platform: UNKNOWN
|
|
||||||
|
|
||||||
UNKNOWN
|
|
||||||
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
pyproject.toml
|
|
||||||
UNKNOWN.egg-info/PKG-INFO
|
|
||||||
UNKNOWN.egg-info/SOURCES.txt
|
|
||||||
UNKNOWN.egg-info/dependency_links.txt
|
|
||||||
UNKNOWN.egg-info/top_level.txt
|
|
||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -14,10 +14,10 @@ SERVICES_CRUD = {
|
|||||||
"create": {
|
"create": {
|
||||||
"name": "create_service_record",
|
"name": "create_service_record",
|
||||||
"description": "Create a new service record for the current user",
|
"description": "Create a new service record for the current user",
|
||||||
"parameters": ["user_id", "name", "service_url", "description"],
|
"parameters": ["user_id", "name", "service_url", "description", "apikey"],
|
||||||
"sql_template": """
|
"sql_template": """
|
||||||
INSERT INTO services (id, user_id, name, service_url, description, status, created_at, updated_at)
|
INSERT INTO services (id, user_id, name, service_url, description, apikey, status, created_at, updated_at)
|
||||||
VALUES (${id}$, ${user_id}$, ${name}$, ${service_url}$, ${description}$, ${status}$, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
VALUES (${id}$, ${user_id}$, ${name}$, ${service_url}$, ${description}$, ${apikey}$, ${status}$, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
""",
|
""",
|
||||||
"return_fields": ["id"]
|
"return_fields": ["id"]
|
||||||
},
|
},
|
||||||
@ -26,35 +26,36 @@ SERVICES_CRUD = {
|
|||||||
"description": "Get all services for the current user",
|
"description": "Get all services for the current user",
|
||||||
"parameters": ["user_id"],
|
"parameters": ["user_id"],
|
||||||
"sql_template": """
|
"sql_template": """
|
||||||
SELECT id, user_id, name, service_url, description, status,
|
SELECT id, user_id, name, service_url, description, apikey, status,
|
||||||
created_at, updated_at
|
created_at, updated_at
|
||||||
FROM services
|
FROM services
|
||||||
WHERE user_id = ${user_id}$
|
WHERE user_id = ${user_id}$
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
""",
|
""",
|
||||||
"return_fields": ["id", "user_id", "name", "service_url", "description", "status", "created_at", "updated_at"]
|
"return_fields": ["id", "user_id", "name", "service_url", "description", "apikey", "status", "created_at", "updated_at"]
|
||||||
},
|
},
|
||||||
"read_by_id": {
|
"read_by_id": {
|
||||||
"name": "get_service_by_id_and_user",
|
"name": "get_service_by_id_and_user",
|
||||||
"description": "Get a specific service by ID for the current user",
|
"description": "Get a specific service by ID for the current user",
|
||||||
"parameters": ["service_id", "user_id"],
|
"parameters": ["service_id", "user_id"],
|
||||||
"sql_template": """
|
"sql_template": """
|
||||||
SELECT id, user_id, name, service_url, description, status,
|
SELECT id, user_id, name, service_url, description, apikey, status,
|
||||||
created_at, updated_at
|
created_at, updated_at
|
||||||
FROM services
|
FROM services
|
||||||
WHERE id = ${service_id}$ AND user_id = ${user_id}$
|
WHERE id = ${service_id}$ AND user_id = ${user_id}$
|
||||||
""",
|
""",
|
||||||
"return_fields": ["id", "user_id", "name", "service_url", "description", "status", "created_at", "updated_at"]
|
"return_fields": ["id", "user_id", "name", "service_url", "description", "apikey", "status", "created_at", "updated_at"]
|
||||||
},
|
},
|
||||||
"update": {
|
"update": {
|
||||||
"name": "update_service_record",
|
"name": "update_service_record",
|
||||||
"description": "Update an existing service record",
|
"description": "Update an existing service record",
|
||||||
"parameters": ["service_id", "user_id", "name", "service_url", "description", "status"],
|
"parameters": ["service_id", "user_id", "name", "service_url", "description", "apikey", "status"],
|
||||||
"sql_template": """
|
"sql_template": """
|
||||||
UPDATE services
|
UPDATE services
|
||||||
SET name = ${name}$,
|
SET name = ${name}$,
|
||||||
service_url = ${service_url}$,
|
service_url = ${service_url}$,
|
||||||
description = ${description}$,
|
description = ${description}$,
|
||||||
|
apikey = ${apikey}$,
|
||||||
status = ${status}$,
|
status = ${status}$,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = ${service_id}$ AND user_id = ${user_id}$
|
WHERE id = ${service_id}$ AND user_id = ${user_id}$
|
||||||
|
|||||||
@ -42,6 +42,13 @@ SERVICES_TABLE = {
|
|||||||
"nullable": True,
|
"nullable": True,
|
||||||
"description": "Optional service description"
|
"description": "Optional service description"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "apikey",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"nullable": True,
|
||||||
|
"default": "''",
|
||||||
|
"description": "API key for service authentication (X-API-Key header)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "status",
|
"name": "status",
|
||||||
"type": "varchar(20)",
|
"type": "varchar(20)",
|
||||||
|
|||||||
@ -18,14 +18,13 @@ from datetime import datetime
|
|||||||
# Import sqlor database module
|
# Import sqlor database module
|
||||||
from sqlor.dbpools import get_sor_context
|
from sqlor.dbpools import get_sor_context
|
||||||
|
|
||||||
# Import user context helper
|
>>>>>>> f741c58 (feat(hermes-web-cli): refactor user context, settings, services and sessions management)
|
||||||
from appPublic.uniqueID import getID
|
|
||||||
|
|
||||||
# Import database table definitions and CRUD operations
|
# Import database table definitions and CRUD operations
|
||||||
from .db_tables import TABLE_DEFINITIONS
|
from .db_tables import TABLE_DEFINITIONS
|
||||||
from .crud_ops import SERVICES_CRUD, SESSIONS_CRUD, SETTINGS_CRUD
|
from .crud_ops import SERVICES_CRUD, SESSIONS_CRUD, SETTINGS_CRUD
|
||||||
|
|
||||||
def load_hermes_web_cli():
|
def load_hermes_web_cli():
|
||||||
|
<<<<<<< HEAD
|
||||||
"""Initialize and load the hermes-web-cli module.
|
"""Initialize and load the hermes-web-cli module.
|
||||||
|
|
||||||
This function is called by Sage system during module loading.
|
This function is called by Sage system during module loading.
|
||||||
@ -465,8 +464,523 @@ async def get_session_by_id(userid, session_id: str) -> Optional[Dict]:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting session by ID: {str(e)}")
|
print(f"Error getting session by ID: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
=======
|
||||||
|
"""Initialize and load the hermes-web-cli module.
|
||||||
|
|
||||||
# Utility functions for validation
|
This function is called by Sage system during module loading.
|
||||||
|
It registers all module functions with the ServerEnv instance
|
||||||
|
so they can be called directly from .ui and .dspy files.
|
||||||
|
"""
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
|
||||||
|
# Initialize database tables if needed
|
||||||
|
try:
|
||||||
|
from .init_db import init_database
|
||||||
|
import asyncio
|
||||||
|
# Run database initialization in a new event loop if needed
|
||||||
|
try:
|
||||||
|
asyncio.get_running_loop()
|
||||||
|
# If we're already in an async context, create a task
|
||||||
|
asyncio.create_task(init_database())
|
||||||
|
except RuntimeError:
|
||||||
|
# No running loop, run synchronously
|
||||||
|
asyncio.run(init_database())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Database initialization failed: {str(e)}")
|
||||||
|
# Continue loading even if DB init fails - functions will handle errors gracefully
|
||||||
|
|
||||||
|
# Get the ServerEnv instance
|
||||||
|
env = ServerEnv()
|
||||||
|
|
||||||
|
# Register all module functions with ServerEnv
|
||||||
|
env.get_setting = get_setting
|
||||||
|
env.save_setting = save_setting
|
||||||
|
env.get_all_services = get_all_services
|
||||||
|
env.create_service = create_service
|
||||||
|
env.delete_service = delete_service
|
||||||
|
env.get_service_by_id = get_service_by_id
|
||||||
|
env.test_service_connection = test_service_connection
|
||||||
|
env.create_session = create_session
|
||||||
|
env.send_message_to_service = send_message_to_service
|
||||||
|
env.get_session_messages = get_session_messages
|
||||||
|
env.get_active_sessions = get_active_sessions
|
||||||
|
env.get_recent_sessions = get_recent_sessions
|
||||||
|
env.get_session_by_id = get_session_by_id
|
||||||
|
env.validate_service_url = validate_service_url
|
||||||
|
env.generate_session_id = generate_session_id
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Database operations using sqlor-database-module
|
||||||
|
async def get_all_services(user_id: str) -> List[Dict]:
|
||||||
|
"""Get all registered Hermes services for the specified user from database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The ID of the user whose services to retrieve
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of service dictionaries belonging to the specified user
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Query services table with user_id filter using sqlor-database-module
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SERVICES_CRUD['operations']['read_all']['sql_template']
|
||||||
|
recs = await sor.sqlExe(sql_template, {'user_id': user_id})
|
||||||
|
|
||||||
|
# Convert datetime objects to ISO format strings for JSON serialization
|
||||||
|
result = []
|
||||||
|
for rec in recs:
|
||||||
|
service_dict = dict(rec)
|
||||||
|
if 'created_at' in service_dict and service_dict['created_at']:
|
||||||
|
service_dict['created_at'] = service_dict['created_at'].isoformat()
|
||||||
|
if 'updated_at' in service_dict and service_dict['updated_at']:
|
||||||
|
service_dict['updated_at'] = service_dict['updated_at'].isoformat()
|
||||||
|
result.append(service_dict)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting services: {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def create_service(name: str, url: str, user_id: str, description: str = "", apikey: str = "") -> str:
|
||||||
|
"""Create a new Hermes service registration for the specified user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Service name
|
||||||
|
url: Service URL
|
||||||
|
user_id: The ID of the user creating the service
|
||||||
|
description: Service description (optional)
|
||||||
|
apikey: API key for the service (optional)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created service ID
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Validate service URL
|
||||||
|
if not await validate_service_url(url):
|
||||||
|
raise ValueError("Invalid service URL")
|
||||||
|
|
||||||
|
service_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Save to database using sqlor-database-module
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SERVICES_CRUD['operations']['create']['sql_template']
|
||||||
|
await sor.sqlExe(sql_template, {
|
||||||
|
'id': service_id,
|
||||||
|
'user_id': user_id,
|
||||||
|
'name': name,
|
||||||
|
'service_url': url,
|
||||||
|
'description': description,
|
||||||
|
'apikey': apikey,
|
||||||
|
'status': 'active'
|
||||||
|
})
|
||||||
|
|
||||||
|
return service_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating service: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def delete_service(service_id: str, user_id: str) -> bool:
|
||||||
|
"""Delete a Hermes service registration (only if owned by specified user).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_id: The ID of the service to delete
|
||||||
|
user_id: The ID of the user attempting deletion
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if deleted successfully, False otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Verify service belongs to current user before deletion
|
||||||
|
service = await get_service_by_id(service_id, user_id)
|
||||||
|
if not service:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if service.get("user_id") != user_id:
|
||||||
|
print(f"Permission denied: Service {service_id} does not belong to user {user_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Delete from database using sqlor-database-module
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SERVICES_CRUD['operations']['delete']['sql_template']
|
||||||
|
await sor.sqlExe(sql_template, {
|
||||||
|
'service_id': service_id,
|
||||||
|
'user_id': user_id
|
||||||
|
})
|
||||||
|
|
||||||
|
# Also delete associated sessions
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
await sor.sqlExe("""
|
||||||
|
DELETE FROM sessions
|
||||||
|
WHERE service_id = ${service_id}$ AND user_id = ${user_id}$
|
||||||
|
""", {
|
||||||
|
'service_id': service_id,
|
||||||
|
'user_id': user_id
|
||||||
|
})
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error deleting service: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_service_by_id(service_id: str, user_id: str) -> Optional[Dict]:
|
||||||
|
"""Get service configuration by ID (only if owned by specified user).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_id: The ID of the service to retrieve
|
||||||
|
user_id: The ID of the user requesting the service
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Service dictionary if found and owned by user, None otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Query database directly with user_id filter for security
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SERVICES_CRUD['operations']['read_by_id']['sql_template']
|
||||||
|
recs = await sor.sqlExe(sql_template, {
|
||||||
|
'service_id': service_id,
|
||||||
|
'user_id': user_id
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(recs) > 0:
|
||||||
|
service_dict = dict(recs[0])
|
||||||
|
if 'created_at' in service_dict and service_dict['created_at']:
|
||||||
|
service_dict['created_at'] = service_dict['created_at'].isoformat()
|
||||||
|
if 'updated_at' in service_dict and service_dict['updated_at']:
|
||||||
|
service_dict['updated_at'] = service_dict['updated_at'].isoformat()
|
||||||
|
return service_dict
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting service: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Service connection testing
|
||||||
|
async def test_service_connection(service_id: str) -> Tuple[bool, str]:
|
||||||
|
"""Test connection to a Hermes service endpoint.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_id: The ID of the service to test
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, str]: (is_connected, status_message)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get service configuration from database
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SERVICES_CRUD['operations']['read_by_id']['sql_template']
|
||||||
|
recs = await sor.sqlExe(sql_template, {
|
||||||
|
'service_id': service_id,
|
||||||
|
'user_id': ''
|
||||||
|
})
|
||||||
|
if not recs:
|
||||||
|
return False, "Service not found"
|
||||||
|
service = dict(recs[0])
|
||||||
|
|
||||||
|
url = service["service_url"]
|
||||||
|
apikey = service.get("apikey", "")
|
||||||
|
|
||||||
|
# Prepare headers
|
||||||
|
headers = {}
|
||||||
|
if apikey:
|
||||||
|
headers["Authorization"] = f"Bearer {apikey}"
|
||||||
|
|
||||||
|
# Test the /health endpoint or similar
|
||||||
|
timeout = aiohttp.ClientTimeout(total=10)
|
||||||
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
|
async with session.get(f"{url.rstrip('/')}/health", headers=headers) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
return True, "Connected"
|
||||||
|
else:
|
||||||
|
return False, f"HTTP {response.status}"
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return False, "Connection timeout"
|
||||||
|
except aiohttp.ClientConnectorError:
|
||||||
|
return False, "Connection refused"
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Error: {str(e)}"
|
||||||
|
|
||||||
|
# Session management
|
||||||
|
async def create_session(service_id: str, user_id: str, user_message: str = "") -> str:
|
||||||
|
"""Create a new session with a Hermes service."""
|
||||||
|
try:
|
||||||
|
# Get service configuration (verify it belongs to current user)
|
||||||
|
service = await get_service_by_id(service_id, user_id)
|
||||||
|
if not service:
|
||||||
|
raise ValueError(f"Service {service_id} not found or access denied")
|
||||||
|
|
||||||
|
service_url = service["service_url"]
|
||||||
|
apikey = service.get("apikey", "")
|
||||||
|
|
||||||
|
# Prepare headers
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add Authorization header if API key is provided
|
||||||
|
if apikey:
|
||||||
|
headers["Authorization"] = f"Bearer {apikey}"
|
||||||
|
|
||||||
|
# Call remote service API to create session
|
||||||
|
timeout = aiohttp.ClientTimeout(total=30)
|
||||||
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
|
async with session.post(
|
||||||
|
f"{service_url.rstrip('/')}/api/v1/sessions",
|
||||||
|
json={
|
||||||
|
"user_id": user_id,
|
||||||
|
"initial_message": user_message if user_message else None
|
||||||
|
},
|
||||||
|
headers=headers
|
||||||
|
) as response:
|
||||||
|
response.raise_for_status()
|
||||||
|
result = await response.json()
|
||||||
|
|
||||||
|
# Get the session ID from the remote service
|
||||||
|
remote_session_id = result.get("session_id", "")
|
||||||
|
if not remote_session_id:
|
||||||
|
raise ValueError("Remote service did not return a session ID")
|
||||||
|
|
||||||
|
# Create local session record in database
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SESSIONS_CRUD['operations']['create']['sql_template']
|
||||||
|
await sor.sqlExe(sql_template, {
|
||||||
|
'session_id': remote_session_id,
|
||||||
|
'user_id': user_id,
|
||||||
|
'service_id': service_id,
|
||||||
|
'session_name': None,
|
||||||
|
'service_name': service.get("name", "Unknown Service")
|
||||||
|
})
|
||||||
|
|
||||||
|
# Return the session ID from the remote service
|
||||||
|
return remote_session_id
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating session: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def send_message_to_service(service_id: str, session_id: str, message: str, user_id: str) -> Dict:
|
||||||
|
"""Send a message to a Hermes service and get response (only if session owned by specified user).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service_id: The service ID
|
||||||
|
session_id: The session ID
|
||||||
|
message: The message to send
|
||||||
|
user_id: The ID of the user sending the message
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response from the service
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Verify session belongs to current user before sending message
|
||||||
|
session = await get_session_by_id(session_id, user_id)
|
||||||
|
if not session:
|
||||||
|
raise ValueError(f"Session {session_id} not found or access denied for user {user_id}")
|
||||||
|
|
||||||
|
service = await get_service_by_id(session['service_id'], user_id)
|
||||||
|
if not service:
|
||||||
|
raise ValueError(f"Service for session {session_id} not found or access denied for user {user_id}")
|
||||||
|
|
||||||
|
service_url = service["service_url"]
|
||||||
|
apikey = service.get("apikey", "")
|
||||||
|
|
||||||
|
# Prepare headers
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add Authorization header if API key is provided
|
||||||
|
if apikey:
|
||||||
|
headers["Authorization"] = f"Bearer {apikey}"
|
||||||
|
|
||||||
|
# Call remote service API
|
||||||
|
timeout = aiohttp.ClientTimeout(total=30)
|
||||||
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
|
async with session.post(
|
||||||
|
f"{service_url.rstrip('/')}/api/v1/sessions/{session_id}/messages",
|
||||||
|
json={
|
||||||
|
"message": message
|
||||||
|
},
|
||||||
|
headers=headers
|
||||||
|
) as response:
|
||||||
|
response.raise_for_status()
|
||||||
|
return await response.json()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending message: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def get_session_messages(session_id: str, user_id: str) -> List[Dict]:
|
||||||
|
"""Get all messages for a session (only if session owned by specified user).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session_id: The session ID
|
||||||
|
user_id: The ID of the user requesting messages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of message dictionaries
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Verify session belongs to current user before getting messages
|
||||||
|
session = await get_session_by_id(session_id)
|
||||||
|
if not session:
|
||||||
|
print(f"Session {session_id} not found or access denied for user {user_id}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Get the associated service
|
||||||
|
service = await get_service_by_id(session['service_id'], user_id)
|
||||||
|
if not service:
|
||||||
|
print(f"Service for session {session_id} not found or access denied")
|
||||||
|
return []
|
||||||
|
|
||||||
|
service_url = service["service_url"]
|
||||||
|
apikey = service.get("apikey", "")
|
||||||
|
|
||||||
|
# Prepare headers
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add Authorization header if API key is provided
|
||||||
|
if apikey:
|
||||||
|
headers["Authorization"] = f"Bearer {apikey}"
|
||||||
|
|
||||||
|
# Call remote service API to get messages
|
||||||
|
timeout = aiohttp.ClientTimeout(total=30)
|
||||||
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
|
async with session.get(
|
||||||
|
f"{service_url.rstrip('/')}/api/v1/sessions/{session_id}/messages",
|
||||||
|
headers=headers
|
||||||
|
) as response:
|
||||||
|
response.raise_for_status()
|
||||||
|
messages = await response.json()
|
||||||
|
|
||||||
|
# Update session last_active timestamp and message count in local database
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
await sor.sqlExe("""
|
||||||
|
UPDATE sessions
|
||||||
|
SET last_active = CURRENT_TIMESTAMP,
|
||||||
|
message_count = ${message_count}$
|
||||||
|
WHERE session_id = ${session_id}$ AND user_id = ${user_id}$
|
||||||
|
""", {
|
||||||
|
'session_id': session_id,
|
||||||
|
'user_id': user_id,
|
||||||
|
'message_count': len(messages)
|
||||||
|
})
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting session messages: {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_active_sessions(user_id: str) -> List[Dict]:
|
||||||
|
"""Get all active sessions for the specified user from database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The ID of the user whose active sessions to retrieve
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of active session dictionaries
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Query the sessions table for active sessions belonging to current user using sqlor-database-module
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SESSIONS_CRUD['operations']['read_active']['sql_template']
|
||||||
|
recs = await sor.sqlExe(sql_template, {'user_id': user_id})
|
||||||
|
|
||||||
|
# Convert datetime objects to ISO format strings for JSON serialization
|
||||||
|
result = []
|
||||||
|
for rec in recs:
|
||||||
|
session_dict = dict(rec)
|
||||||
|
if 'created_at' in session_dict and session_dict['created_at']:
|
||||||
|
session_dict['created_at'] = session_dict['created_at'].isoformat()
|
||||||
|
if 'last_active' in session_dict and session_dict['last_active']:
|
||||||
|
session_dict['last_active'] = session_dict['last_active'].isoformat()
|
||||||
|
result.append(session_dict)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting active sessions: {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_recent_sessions(user_id: str, limit: int = 5) -> List[Dict]:
|
||||||
|
"""Get recent sessions for the specified user from database, ordered by creation time (most recent first).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: The ID of the user whose recent sessions to retrieve
|
||||||
|
limit: Maximum number of sessions to return (default: 5)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of recent session dictionaries
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Query the sessions table for recent sessions belonging to current user using sqlor-database-module
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SESSIONS_CRUD['operations']['read_recent']['sql_template']
|
||||||
|
recs = await sor.sqlExe(sql_template, {'user_id': user_id, 'limit': limit})
|
||||||
|
|
||||||
|
# Convert datetime objects to ISO format strings for JSON serialization
|
||||||
|
result = []
|
||||||
|
for rec in recs:
|
||||||
|
session_dict = dict(rec)
|
||||||
|
if 'created_at' in session_dict and session_dict['created_at']:
|
||||||
|
session_dict['created_at'] = session_dict['created_at'].isoformat()
|
||||||
|
if 'last_active' in session_dict and session_dict['last_active']:
|
||||||
|
session_dict['last_active'] = session_dict['last_active'].isoformat()
|
||||||
|
result.append(session_dict)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting recent sessions: {str(e)}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_session_by_id(session_id: str, user_id: str) -> Optional[Dict]:
|
||||||
|
"""Get session details by session ID (only if owned by specified user).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session_id: The session ID to retrieve
|
||||||
|
user_id: The ID of the user requesting the session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Session dictionary if found and owned by user, None otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Query database directly with user_id filter for security
|
||||||
|
db = DBPools()
|
||||||
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
|
sql_template = SESSIONS_CRUD['operations']['read_by_id']['sql_template']
|
||||||
|
recs = await sor.sqlExe(sql_template, {
|
||||||
|
'session_id': session_id,
|
||||||
|
'user_id': user_id
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(recs) > 0:
|
||||||
|
session_dict = dict(recs[0])
|
||||||
|
if 'created_at' in session_dict and session_dict['created_at']:
|
||||||
|
session_dict['created_at'] = session_dict['created_at'].isoformat()
|
||||||
|
if 'last_active' in session_dict and session_dict['last_active']:
|
||||||
|
session_dict['last_active'] = session_dict['last_active'].isoformat()
|
||||||
|
return session_dict
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting session by ID: {str(e)}")
|
||||||
|
return None
|
||||||
|
# Database operations using sqlor-database-module
|
||||||
def validate_service_url(url: str) -> bool:
|
def validate_service_url(url: str) -> bool:
|
||||||
"""Validate if a URL is a valid Hermes service endpoint."""
|
"""Validate if a URL is a valid Hermes service endpoint."""
|
||||||
if not url.startswith(('http://', 'https://')):
|
if not url.startswith(('http://', 'https://')):
|
||||||
@ -480,12 +994,16 @@ def generate_session_id() -> str:
|
|||||||
return getID()
|
return getID()
|
||||||
|
|
||||||
# Settings management
|
# Settings management
|
||||||
async def get_setting() -> Dict:
|
async def get_setting(user_id: str) -> Dict:
|
||||||
"""Get current user settings from database or return defaults."""
|
"""Get user settings from database or return defaults.
|
||||||
import json
|
|
||||||
|
|
||||||
# Get current user ID
|
Args:
|
||||||
user_id = await get_current_user_id()
|
user_id: The ID of the user whose settings to retrieve
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
User settings dictionary
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
default_settings = {
|
default_settings = {
|
||||||
"security": {
|
"security": {
|
||||||
@ -504,8 +1022,8 @@ async def get_setting() -> Dict:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Query user settings from database
|
# Query user settings from database
|
||||||
env = ServerEnv()
|
db = DBPools()
|
||||||
async with get_sor_context(env, 'hermes-web-cli') as sor:
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
sql_template = SETTINGS_CRUD['operations']['read']['sql_template']
|
sql_template = SETTINGS_CRUD['operations']['read']['sql_template']
|
||||||
recs = await sor.sqlExe(sql_template, {'user_id': user_id})
|
recs = await sor.sqlExe(sql_template, {'user_id': user_id})
|
||||||
|
|
||||||
@ -529,16 +1047,22 @@ async def get_setting() -> Dict:
|
|||||||
|
|
||||||
return default_settings
|
return default_settings
|
||||||
|
|
||||||
async def save_setting(section: str, key: str, value) -> bool:
|
async def save_setting(section: str, key: str, value, user_id: str) -> bool:
|
||||||
"""Save a specific setting value to current user's database record."""
|
"""Save a specific setting value to user\'s database record.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
section: Settings section name
|
||||||
|
key: Setting key name
|
||||||
|
value: Setting value
|
||||||
|
user_id: The ID of the user whose settings to update
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if saved successfully, False otherwise
|
||||||
|
"""
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Get current user ID
|
|
||||||
user_id = await get_current_user_id()
|
|
||||||
|
|
||||||
# Load existing settings or start with defaults
|
# Load existing settings or start with defaults
|
||||||
settings = await get_setting()
|
settings = await get_setting(user_id)
|
||||||
|
|
||||||
# Update the specific setting
|
# Update the specific setting
|
||||||
if section not in settings:
|
if section not in settings:
|
||||||
settings[section] = {}
|
settings[section] = {}
|
||||||
@ -546,8 +1070,8 @@ async def save_setting(section: str, key: str, value) -> bool:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Save to database using sqlor-database-module
|
# Save to database using sqlor-database-module
|
||||||
env = ServerEnv()
|
db = DBPools()
|
||||||
async with get_sor_context(env, 'hermes-web-cli') as sor:
|
async with db.sqlorContext('hermes-web-cli') as sor:
|
||||||
sql_template = SETTINGS_CRUD['operations']['create_or_update']['sql_template']
|
sql_template = SETTINGS_CRUD['operations']['create_or_update']['sql_template']
|
||||||
await sor.sqlExe(sql_template, {
|
await sor.sqlExe(sql_template, {
|
||||||
'user_id': user_id,
|
'user_id': user_id,
|
||||||
@ -557,10 +1081,9 @@ async def save_setting(section: str, key: str, value) -> bool:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error saving settings: {str(e)}")
|
print(f"Error saving settings: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Module metadata
|
# Module metadata
|
||||||
MODULE_NAME = "hermes-web-cli"
|
MODULE_NAME = "hermes-web-cli"
|
||||||
MODULE_VERSION = "0.1.0"
|
MODULE_VERSION = "0.2.0"
|
||||||
|
|
||||||
# Export all public functions
|
# Export all public functions
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -580,7 +1103,6 @@ __all__ = [
|
|||||||
'generate_session_id',
|
'generate_session_id',
|
||||||
'get_setting',
|
'get_setting',
|
||||||
'save_setting',
|
'save_setting',
|
||||||
'get_current_user_id',
|
|
||||||
'MODULE_NAME',
|
'MODULE_NAME',
|
||||||
'MODULE_VERSION'
|
'MODULE_VERSION'
|
||||||
]
|
]
|
||||||
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "hermes-web-cli"
|
name = "hermes-web-cli"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
description = "Hermes Web CLI module for multi-service management"
|
description = "Hermes Web CLI module for multi-service management"
|
||||||
authors = [{name = "Your Name", email = "your.email@example.com"}]
|
authors = [{name = "Your Name", email = "your.email@example.com"}]
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
|
|||||||
153
test_multiuser.py
Normal file
153
test_multiuser.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script to verify multi-user data isolation in hermes-web-cli module.
|
||||||
|
This script simulates multiple users and verifies that they can only access their own data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Add the module path to Python path
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
|
||||||
|
from hermes_web_cli.init import (
|
||||||
|
get_all_services, create_service, delete_service, get_service_by_id,
|
||||||
|
create_session, get_active_sessions, get_recent_sessions, get_session_by_id,
|
||||||
|
get_setting, save_setting
|
||||||
|
)
|
||||||
|
from hermes_web_cli.user_context import get_current_user_id
|
||||||
|
|
||||||
|
# Mock ahserver's get_user function for testing
|
||||||
|
async def mock_get_user_1():
|
||||||
|
return "user1"
|
||||||
|
|
||||||
|
async def mock_get_user_2():
|
||||||
|
return "user2"
|
||||||
|
|
||||||
|
async def test_multiuser_isolation():
|
||||||
|
"""Test that users can only access their own data."""
|
||||||
|
print("Testing multi-user data isolation...")
|
||||||
|
|
||||||
|
# Test user 1
|
||||||
|
print("\n--- Testing User 1 ---")
|
||||||
|
|
||||||
|
# Mock user context for user 1
|
||||||
|
import hermes_web_cli.user_context
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
|
||||||
|
# Create service for user 1
|
||||||
|
service1_id = await create_service(
|
||||||
|
name="User1 Service",
|
||||||
|
url="http://localhost:8080",
|
||||||
|
description="Service for user 1",
|
||||||
|
apikey="user1-key"
|
||||||
|
)
|
||||||
|
print(f"Created service for user 1: {service1_id}")
|
||||||
|
|
||||||
|
# Get services for user 1
|
||||||
|
services1 = await get_all_services()
|
||||||
|
print(f"User 1 sees {len(services1)} services")
|
||||||
|
|
||||||
|
# Verify user 1 can access their service
|
||||||
|
service1 = await get_service_by_id(service1_id)
|
||||||
|
assert service1 is not None, "User 1 should be able to access their own service"
|
||||||
|
print("User 1 can access their own service ✓")
|
||||||
|
|
||||||
|
# Test user 2
|
||||||
|
print("\n--- Testing User 2 ---")
|
||||||
|
|
||||||
|
# Mock user context for user 2
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_2
|
||||||
|
|
||||||
|
# Create service for user 2
|
||||||
|
service2_id = await create_service(
|
||||||
|
name="User2 Service",
|
||||||
|
url="http://localhost:8081",
|
||||||
|
description="Service for user 2",
|
||||||
|
apikey="user2-key"
|
||||||
|
)
|
||||||
|
print(f"Created service for user 2: {service2_id}")
|
||||||
|
|
||||||
|
# Get services for user 2
|
||||||
|
services2 = await get_all_services()
|
||||||
|
print(f"User 2 sees {len(services2)} services")
|
||||||
|
|
||||||
|
# Verify user 2 can access their service
|
||||||
|
service2 = await get_service_by_id(service2_id)
|
||||||
|
assert service2 is not None, "User 2 should be able to access their own service"
|
||||||
|
print("User 2 can access their own service ✓")
|
||||||
|
|
||||||
|
# Verify user 2 cannot access user 1's service
|
||||||
|
service1_from_user2 = await get_service_by_id(service1_id)
|
||||||
|
assert service1_from_user2 is None, "User 2 should NOT be able to access user 1's service"
|
||||||
|
print("User 2 cannot access user 1's service ✓")
|
||||||
|
|
||||||
|
# Verify user 1 still only sees their own service
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
services1_after = await get_all_services()
|
||||||
|
assert len(services1_after) == 1, "User 1 should still only see 1 service"
|
||||||
|
print("User 1 still only sees their own service ✓")
|
||||||
|
|
||||||
|
# Test sessions
|
||||||
|
print("\n--- Testing Session Isolation ---")
|
||||||
|
|
||||||
|
# Create session for user 1
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
session1_id = await create_session(service1_id, "user1", "Hello from user 1")
|
||||||
|
print(f"Created session for user 1: {session1_id}")
|
||||||
|
|
||||||
|
# Create session for user 2
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_2
|
||||||
|
session2_id = await create_session(service2_id, "user2", "Hello from user 2")
|
||||||
|
print(f"Created session for user 2: {session2_id}")
|
||||||
|
|
||||||
|
# Verify user 1 only sees their session
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
sessions1 = await get_active_sessions()
|
||||||
|
assert len(sessions1) == 1, "User 1 should only see 1 session"
|
||||||
|
assert sessions1[0]['session_id'] == session1_id, "User 1 should see their own session"
|
||||||
|
print("User 1 session isolation ✓")
|
||||||
|
|
||||||
|
# Verify user 2 only sees their session
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_2
|
||||||
|
sessions2 = await get_active_sessions()
|
||||||
|
assert len(sessions2) == 1, "User 2 should only see 1 session"
|
||||||
|
assert sessions2[0]['session_id'] == session2_id, "User 2 should see their own session"
|
||||||
|
print("User 2 session isolation ✓")
|
||||||
|
|
||||||
|
# Test settings
|
||||||
|
print("\n--- Testing Settings Isolation ---")
|
||||||
|
|
||||||
|
# Save setting for user 1
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
await save_setting("appearance", "theme", "dark")
|
||||||
|
|
||||||
|
# Save setting for user 2
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_2
|
||||||
|
await save_setting("appearance", "theme", "light")
|
||||||
|
|
||||||
|
# Verify user 1 gets their setting
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
settings1 = await get_setting()
|
||||||
|
assert settings1.get('appearance', {}).get('theme') == 'dark', "User 1 should have dark theme"
|
||||||
|
print("User 1 settings isolation ✓")
|
||||||
|
|
||||||
|
# Verify user 2 gets their setting
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_2
|
||||||
|
settings2 = await get_setting()
|
||||||
|
assert settings2.get('appearance', {}).get('theme') == 'light', "User 2 should have light theme"
|
||||||
|
print("User 2 settings isolation ✓")
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
print("\n--- Cleaning Up ---")
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_1
|
||||||
|
await delete_service(service1_id)
|
||||||
|
|
||||||
|
hermes_web_cli.user_context.get_user = mock_get_user_2
|
||||||
|
await delete_service(service2_id)
|
||||||
|
|
||||||
|
print("\n✅ All multi-user isolation tests passed!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(test_multiuser_isolation())
|
||||||
@ -2,10 +2,11 @@
|
|||||||
# This .dspy file uses functions released by load_hermes_web_cli()
|
# This .dspy file uses functions released by load_hermes_web_cli()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use the function provided by hermes-web-cli module
|
# Get current user ID using ahserver's built-in get_user() function
|
||||||
userid = await get_user()
|
user_id = await get_user()
|
||||||
services = await get_all_services(userid)
|
|
||||||
|
|
||||||
|
# Use the function provided by hermes-web-cli module
|
||||||
|
services = await get_all_services(user_id)
|
||||||
# Format for code component (value, text pairs)
|
# Format for code component (value, text pairs)
|
||||||
result = []
|
result = []
|
||||||
for service in services:
|
for service in services:
|
||||||
|
|||||||
@ -2,8 +2,11 @@
|
|||||||
# This .dspy file uses functions provided by load_hermes_web_cli()
|
# This .dspy file uses functions provided by load_hermes_web_cli()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Get current user ID using ahserver's built-in get_user() function
|
||||||
|
user_id = await get_user()
|
||||||
|
|
||||||
# Use the function provided by the hermes-web-cli module
|
# Use the function provided by the hermes-web-cli module
|
||||||
services = await get_all_services()
|
services = await get_all_services(user_id)
|
||||||
|
|
||||||
# Format services for UI display
|
# Format services for UI display
|
||||||
result = []
|
result = []
|
||||||
|
|||||||
@ -5,10 +5,12 @@ try:
|
|||||||
service_id = params_kw.get('id')
|
service_id = params_kw.get('id')
|
||||||
if not service_id:
|
if not service_id:
|
||||||
return {"error": "Service ID is required"}
|
return {"error": "Service ID is required"}
|
||||||
userid = await get_user()
|
|
||||||
# Call the function provided by the hermes-web-cli module
|
|
||||||
success = await delete_service(userid, service_id)
|
|
||||||
|
|
||||||
|
# Get current user ID using ahserver's built-in get_user() function
|
||||||
|
user_id = await get_user()
|
||||||
|
|
||||||
|
# Call the function provided by the hermes-web-cli module
|
||||||
|
success = await delete_service(service_id, user_id)
|
||||||
if success:
|
if success:
|
||||||
return {"success": True, "message": "Service removed successfully"}
|
return {"success": True, "message": "Service removed successfully"}
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -7,7 +7,7 @@ try:
|
|||||||
return {"error": "Service ID is required"}
|
return {"error": "Service ID is required"}
|
||||||
|
|
||||||
# Call the function provided by the hermes-web-cli module
|
# Call the function provided by the hermes-web-cli module
|
||||||
is_connected, status_msg = test_service_connection(service_id)
|
is_connected, status_msg = await test_service_connection(service_id)
|
||||||
|
|
||||||
return {"status": status_msg}
|
return {"status": status_msg}
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,11 @@
|
|||||||
# This .dspy file uses functions provided by load_hermes_web_cli()
|
# This .dspy file uses functions provided by load_hermes_web_cli()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Get current user ID using ahserver's built-in get_user() function
|
||||||
|
user_id = await get_user()
|
||||||
|
|
||||||
# Use the function provided by the hermes-web-cli module
|
# Use the function provided by the hermes-web-cli module
|
||||||
sessions = await get_active_sessions()
|
sessions = await get_active_sessions(user_id)
|
||||||
|
|
||||||
# Format sessions for UI display
|
# Format sessions for UI display
|
||||||
result = []
|
result = []
|
||||||
|
|||||||
@ -16,7 +16,8 @@
|
|||||||
"marginBottom": "20px"
|
"marginBottom": "20px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{% set settings_data = get_setting() %}
|
{% set current_user = get_user() %}
|
||||||
|
{% set settings_data = get_setting(current_user) %}
|
||||||
{
|
{
|
||||||
"widgettype": "TabPanel",
|
"widgettype": "TabPanel",
|
||||||
"id": "settings-tabs",
|
"id": "settings-tabs",
|
||||||
|
|||||||
@ -4,9 +4,11 @@
|
|||||||
try:
|
try:
|
||||||
theme = request.form.get('theme', 'dark')
|
theme = request.form.get('theme', 'dark')
|
||||||
|
|
||||||
# Save settings using the module function
|
# Get current user ID
|
||||||
await save_setting('appearance', 'theme', theme)
|
user_id = await get_user()
|
||||||
|
|
||||||
|
# Save settings using the module function
|
||||||
|
await save_setting('appearance', 'theme', theme, user_id)
|
||||||
return {"success": True, "message": "Appearance settings saved successfully"}
|
return {"success": True, "message": "Appearance settings saved successfully"}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -11,11 +11,13 @@ try:
|
|||||||
except:
|
except:
|
||||||
session_timeout = 30
|
session_timeout = 30
|
||||||
|
|
||||||
# Save settings using the module function
|
# Get current user ID
|
||||||
await save_setting('general', 'default_model', default_model)
|
user_id = await get_user()
|
||||||
await save_setting('general', 'session_timeout', session_timeout)
|
|
||||||
await save_setting('general', 'auto_save', auto_save)
|
|
||||||
|
|
||||||
|
# Save settings using the module function
|
||||||
|
await save_setting('general', 'default_model', default_model, user_id)
|
||||||
|
await save_setting('general', 'session_timeout', session_timeout, user_id)
|
||||||
|
await save_setting('general', 'auto_save', auto_save, user_id)
|
||||||
return {"success": True, "message": "General settings saved successfully"}
|
return {"success": True, "message": "General settings saved successfully"}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -5,10 +5,12 @@ try:
|
|||||||
require_auth = request.form.get('require-auth', 'false') == 'true'
|
require_auth = request.form.get('require-auth', 'false') == 'true'
|
||||||
encrypt_storage = request.form.get('encrypt-storage', 'false') == 'true'
|
encrypt_storage = request.form.get('encrypt-storage', 'false') == 'true'
|
||||||
|
|
||||||
# Save settings using the module function
|
# Get current user ID
|
||||||
await save_setting('security', 'require_auth', require_auth)
|
user_id = await get_user()
|
||||||
await save_setting('security', 'encrypt_storage', encrypt_storage)
|
|
||||||
|
|
||||||
|
# Save settings using the module function
|
||||||
|
await save_setting('security', 'require_auth', require_auth, user_id)
|
||||||
|
await save_setting('security', 'encrypt_storage', encrypt_storage, user_id)
|
||||||
return {"success": True, "message": "Security settings saved successfully"}
|
return {"success": True, "message": "Security settings saved successfully"}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user