diff --git a/.env.example b/.env.example index 231f2f5..fa54985 100644 --- a/.env.example +++ b/.env.example @@ -14,10 +14,10 @@ VITE_API_BASE_URL="http://192.168.3.11:8000" # Optional WebSocket override. If unset, it is derived from VITE_API_BASE_URL. VITE_WS_PROGRESS_URL="ws://192.168.3.11:8000/ws/progress" -# Backend SAM runtime defaults. SAM 3 additionally requires the official sam3 -# package, Python 3.12+, PyTorch 2.7+, and a CUDA-capable GPU per Meta's repo. -sam_default_model="sam2" -sam_model_path="/home/wkmgc/Desktop/Seg_Server/models/sam2_hiera_tiny.pt" -sam_model_config="configs/sam2/sam2_hiera_t.yaml" +# Backend SAM runtime defaults. Current product exposes SAM 2.1 variants only. +sam_default_model="sam2.1_hiera_tiny" +sam_model_path="/home/wkmgc/Desktop/Seg_Server/models/sam2.1_hiera_tiny.pt" +sam_model_config="configs/sam2.1/sam2.1_hiera_t.yaml" sam3_model_version="sam3" sam3_checkpoint_path="/home/wkmgc/Desktop/Seg_Server/sam3权重/sam3.pt" +sam3_external_enabled=false diff --git a/backend/config.py b/backend/config.py index 6e48e9e..42da37d 100644 --- a/backend/config.py +++ b/backend/config.py @@ -24,7 +24,7 @@ class Settings(BaseSettings): sam_model_config: str = "configs/sam2.1/sam2.1_hiera_t.yaml" sam3_model_version: str = "sam3" sam3_checkpoint_path: str = "/home/wkmgc/Desktop/Seg_Server/sam3权重/sam3.pt" - sam3_external_enabled: bool = True + sam3_external_enabled: bool = False sam3_external_python: str = "/home/wkmgc/miniconda3/envs/sam3/bin/python" sam3_timeout_seconds: int = 300 sam3_status_cache_seconds: int = 30 diff --git a/backend/main.py b/backend/main.py index 1baf22b..5f7fd63 100644 --- a/backend/main.py +++ b/backend/main.py @@ -46,22 +46,17 @@ def _ensure_runtime_schema_columns() -> None: logger.warning("Runtime schema column check failed: %s", exc) -def _seed_default_admin_and_ownership_sync() -> None: - """Ensure the default admin exists and owns legacy unassigned projects.""" - from models import Project +def _seed_default_admin_sync() -> None: + """Ensure the single default admin exists without rewriting project ownership metadata.""" from routers.auth import ensure_default_admin db = SessionLocal() try: admin = ensure_default_admin(db) - db.query(Project).filter(Project.owner_user_id.is_(None)).update( - {"owner_user_id": admin.id}, - synchronize_session=False, - ) db.commit() - logger.info("Default admin ready; legacy projects assigned to user id=%s", admin.id) + logger.info("Default admin ready id=%s", admin.id) except Exception as exc: # noqa: BLE001 - logger.error("Failed to seed default admin or ownership: %s", exc) + logger.error("Failed to seed default admin: %s", exc) finally: db.close() @@ -82,10 +77,7 @@ def _seed_default_project_sync() -> None: try: admin = ensure_default_admin(db) existing_video = db.query(Project).filter(Project.name == DEMO_VIDEO_PROJECT_NAME).first() - if existing_video is not None and existing_video.owner_user_id is None: - existing_video.owner_user_id = admin.id - db.commit() - elif existing_video is None and os.path.exists(settings.demo_video_path): + if existing_video is None and os.path.exists(settings.demo_video_path): video_project = create_unparsed_video_demo_project( db, owner=admin, @@ -96,9 +88,6 @@ def _seed_default_project_sync() -> None: existing_dicom = db.query(Project).filter(Project.name == DEMO_DICOM_PROJECT_NAME).first() if existing_dicom is not None: - if existing_dicom.owner_user_id is None: - existing_dicom.owner_user_id = admin.id - db.commit() return if not demo_dicom_files(settings.demo_dicom_dir): @@ -147,7 +136,7 @@ async def lifespan(app: FastAPI): try: Base.metadata.create_all(bind=engine) _ensure_runtime_schema_columns() - _seed_default_admin_and_ownership_sync() + _seed_default_admin_sync() logger.info("Database tables initialized.") except Exception as exc: # noqa: BLE001 logger.error("Database initialization failed: %s", exc) diff --git a/backend/models.py b/backend/models.py index e6e7271..268bbcb 100644 --- a/backend/models.py +++ b/backend/models.py @@ -25,7 +25,7 @@ class User(Base): id = Column(Integer, primary_key=True, index=True) username = Column(String(150), unique=True, index=True, nullable=False) password_hash = Column(String(255), nullable=False) - role = Column(String(50), default="admin", nullable=False) + role = Column(String(50), default="annotator", nullable=False) is_active = Column(Integer, default=1, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py index a7cda29..7c257d5 100644 --- a/backend/tests/test_auth.py +++ b/backend/tests/test_auth.py @@ -30,3 +30,37 @@ def test_business_routes_require_auth(app): response = unauthenticated.get("/api/projects") assert response.status_code == 401 + + +def test_default_admin_seed_does_not_claim_legacy_shared_projects(db_session): + from models import Project + from routers.auth import ensure_default_admin + + project = Project(name="Legacy Shared Project", owner_user_id=None) + db_session.add(project) + db_session.commit() + db_session.refresh(project) + + ensure_default_admin(db_session) + db_session.refresh(project) + + assert project.owner_user_id is None + + +def test_user_model_default_role_is_annotator(db_session): + from models import User + from routers.auth import hash_password + + user = User(username="script-created", password_hash=hash_password("secret123")) + db_session.add(user) + db_session.commit() + db_session.refresh(user) + + assert user.role == "annotator" + + +def test_backend_runtime_defaults_match_current_product(): + from config import settings + + assert settings.sam_default_model == "sam2.1_hiera_tiny" + assert settings.sam3_external_enabled is False diff --git a/doc/02-current-implementation-map.md b/doc/02-current-implementation-map.md index 371beec..480b87a 100644 --- a/doc/02-current-implementation-map.md +++ b/doc/02-current-implementation-map.md @@ -72,7 +72,7 @@ 2. `UserAdmin.tsx` 调用 `GET/POST/PATCH/DELETE /api/admin/users` 完成标注员新增、停用/启用、改密码和删除用户;不提供观察员或第二个管理员入口。 3. `UserAdmin.tsx` 调用 `GET /api/admin/audit-logs` 展示登录成功/失败以及用户管理操作审计。 4. `UserAdmin.tsx` 危险区“恢复演示出厂设置”需要浏览器确认和输入 `RESET_DEMO_FACTORY`,随后调用 `POST /api/admin/demo-factory-reset`。 -5. 后端 `backend/routers/admin.py` 会阻止管理员删除、停用或降级自己,并阻止删除仍拥有项目的用户;演示出厂重置会清空其它用户、项目帧、标注、任务和私有模板,重新创建演示视频项目和一个已按文件名自然顺序生成帧的演示 DICOM 项目。 +5. 后端 `backend/routers/admin.py` 会阻止管理员删除、停用、改名或降级自己;项目库已共享,因此删除标注员不会删除或迁移项目;演示出厂重置会清空其它用户、项目帧、标注、任务和私有模板,重新创建演示视频项目和一个已按文件名自然顺序生成帧的演示 DICOM 项目。 ### 项目与拆帧 diff --git a/doc/10-installation.md b/doc/10-installation.md index c969082..fa217cc 100644 --- a/doc/10-installation.md +++ b/doc/10-installation.md @@ -184,6 +184,7 @@ minio_secure=false sam_default_model=sam2.1_hiera_tiny sam_model_path=/home/wkmgc/Desktop/Seg_Server/models/sam2.1_hiera_tiny.pt sam_model_config=configs/sam2.1/sam2.1_hiera_t.yaml +sam3_external_enabled=false app_env=development cors_origins=["http://localhost:3000","http://127.0.0.1:3000"]