Merge branch 'pr-498'

# Conflicts:
#	backend/services/user_workspace_manager.py
This commit is contained in:
ajaysi
2026-05-23 13:11:34 +05:30
2 changed files with 73 additions and 34 deletions

View File

@@ -13,7 +13,7 @@ from loguru import logger
from sqlalchemy.orm import Session
from sqlalchemy import text
from services.database import init_user_database, ensure_user_workspace_db_directory
from services.database import WORKSPACE_DIR, init_user_database, ensure_user_workspace_db_directory
from services.workspace_dirs import ensure_user_workspace_dirs
from services.workspace_paths import get_workspace_root, get_user_workspace_dir
@@ -39,60 +39,46 @@ class UserWorkspaceManager:
"""Create a complete user workspace with progressive setup."""
try:
logger.info(f"Creating workspace for user {user_id}")
# Sanitize user_id
safe_user_id = self._sanitize_user_id(user_id)
# Check if we're in production and skip file operations if needed
if os.getenv("RENDER") or os.getenv("RAILWAY") or os.getenv("HEROKU"):
logger.info("Production environment detected - skipping file workspace creation")
return {
"user_id": user_id,
"workspace_path": "/tmp/alwrity_workspace/users/user_" + safe_user_id,
"config": self._create_user_config(user_id),
"created_at": datetime.utcnow().isoformat(),
"production_mode": True
}
# Create user-specific directories
# Format: workspaces/workspace_{user_id}
production_env = bool(os.getenv("RENDER") or os.getenv("RAILWAY") or os.getenv("HEROKU"))
filesystem_minimal_mode = bool(os.getenv("ALWRITY_FILESYSTEM_MINIMAL_MODE"))
mode = "filesystem_minimal" if filesystem_minimal_mode else ("production" if production_env else "development")
user_dir = get_user_workspace_dir(user_id)
user_dir.mkdir(parents=True, exist_ok=True)
# Ensure canonical DB directory and migrate legacy layout if needed
self._ensure_workspace_db_directory(user_id)
# Create user-specific directories lazily via centralized helper
user_dir = ensure_user_workspace_dirs(
user_id,
capabilities={"core", "content", "research", "media", "assets"},
)
# Create user-specific configuration
config = self._create_user_config(user_id)
config_file = user_dir / "config" / "user_config.json"
with open(config_file, 'w') as f:
json.dump(config, f, indent=2)
# Create user-specific database tables
# Use database.py's init_user_database to ensure proper schema
try:
init_user_database(user_id)
except Exception as db_err:
logger.error(f"Failed to initialize user database: {db_err}")
# We don't raise here to allow workspace creation to proceed,
# but it might be critical. Let's log and continue for now or raise?
# If DB init fails, the app might not work.
raise db_err
logger.info(f"✅ User workspace created: {user_dir}")
dirs_created = ["db", "assets", "media", "content", "config/user_config.json"]
logger.info(
"User workspace created",
mode=mode,
workspace_path=str(user_dir),
dirs_created=dirs_created,
)
return {
"user_id": user_id,
"workspace_path": str(user_dir),
"config": config,
"created_at": datetime.now().isoformat()
"created_at": datetime.now().isoformat(),
"mode": mode,
"dirs_created": dirs_created,
}
except Exception as e:
logger.error(f"Error creating user workspace: {e}")
raise

View File

@@ -0,0 +1,53 @@
from pathlib import Path
from services.user_workspace_manager import UserWorkspaceManager
def _configure_temp_workspace(monkeypatch, tmp_path):
workspace_root = tmp_path / "workspace"
monkeypatch.setattr("services.database.WORKSPACE_DIR", str(workspace_root))
monkeypatch.setattr("services.workspace_dirs.WORKSPACE_DIR", str(workspace_root))
monkeypatch.setattr("services.user_workspace_manager.WORKSPACE_DIR", str(workspace_root))
monkeypatch.setattr("services.user_workspace_manager.init_user_database", lambda user_id: None)
return workspace_root
def _assert_required_contract(user_dir: Path):
assert user_dir.exists()
assert (user_dir / "db").exists()
assert (user_dir / "assets").exists()
assert (user_dir / "media").exists()
assert (user_dir / "content").exists()
assert (user_dir / "config" / "user_config.json").exists()
def test_create_user_workspace_development_contract(monkeypatch, tmp_path):
workspace_root = _configure_temp_workspace(monkeypatch, tmp_path)
monkeypatch.delenv("RENDER", raising=False)
monkeypatch.delenv("RAILWAY", raising=False)
monkeypatch.delenv("HEROKU", raising=False)
monkeypatch.delenv("ALWRITY_FILESYSTEM_MINIMAL_MODE", raising=False)
manager = UserWorkspaceManager(db_session=None)
result = manager.create_user_workspace("dev-user")
expected = workspace_root / "workspace_dev-user"
_assert_required_contract(expected)
assert result["workspace_path"] == str(expected)
assert result["mode"] == "development"
assert {"db", "assets", "media", "content", "config/user_config.json"}.issubset(set(result["dirs_created"]))
def test_create_user_workspace_production_filesystem_minimal_contract(monkeypatch, tmp_path):
workspace_root = _configure_temp_workspace(monkeypatch, tmp_path)
monkeypatch.setenv("RENDER", "1")
monkeypatch.setenv("ALWRITY_FILESYSTEM_MINIMAL_MODE", "1")
manager = UserWorkspaceManager(db_session=None)
result = manager.create_user_workspace("prod-user")
expected = workspace_root / "workspace_prod-user"
_assert_required_contract(expected)
assert result["workspace_path"] == str(expected)
assert result["mode"] == "filesystem_minimal"
assert {"db", "assets", "media", "content", "config/user_config.json"}.issubset(set(result["dirs_created"]))