校准系统默认配置
- 将 User ORM 默认角色改为 annotator,避免脚本直建用户时绕出第二个管理员。 - 启动默认 seed 不再把历史无 owner 项目改写归属 admin,保持共享项目库的历史元数据语义。 - 将 SAM 默认配置和环境模板统一到 SAM 2.1 tiny,并默认关闭历史 SAM3 外部 worker。 - 更新安装/实现文档,并补充默认角色、默认模型和 legacy 项目 owner 不改写的后端测试。
This commit is contained in:
10
.env.example
10
.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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 项目。
|
||||
|
||||
### 项目与拆帧
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user