fix: address bugs, security issues, and convention violations in save-sync

- Fix broken path construction in FSSyncHandler: build_* methods now
  return relative paths; sync_watcher uses paths relative to sync base
  instead of CWD (was completely non-functional in production)
- Fix SSH connection leak in push-pull task: conn.close() now in finally
- Add log.warning for disabled SSH host key verification
- Fix race condition in session operation counter: use atomic SQL
  increment instead of read-then-write
- Extract _increment_session_counter helper, add exc_info to warnings
- Replace legacy session.query() with select() in sync_sessions_handler
- Fix orphaned session: trigger_push_pull now passes session_id to job
- Fix wasteful SSH download when no matched_save exists
- Fix BaseModel import collision in sync.py (pydantic -> project base)
- Fix ORM mutation in UserSchema.from_orm_with_request: set field on
  schema instance instead of mutating live ORM object
- Mask ssh_password and ssh_key_path in DeviceSchema API response
- Fix migration PostgreSQL compatibility: condition ON UPDATE clause
  on MySQL, drop enum in downgrade
- Rename copy-paste artifact rom_user_status_enum
This commit is contained in:
nendo
2026-03-16 10:56:43 +09:00
parent c61f52ebed
commit 55638d15dc
11 changed files with 116 additions and 98 deletions

View File

@@ -75,7 +75,22 @@ class DBSyncSessionsHandler(DBBaseHandler):
.values(**data)
.execution_options(synchronize_session="evaluate")
)
return session.query(SyncSession).filter_by(id=session_id).one()
return session.scalar(select(SyncSession).filter_by(id=session_id))
@begin_session
def increment_operations_completed(
self,
session_id: int,
session: Session = None, # type: ignore
) -> None:
session.execute(
update(SyncSession)
.where(SyncSession.id == session_id)
.values(
operations_completed=SyncSession.operations_completed + 1,
)
.execution_options(synchronize_session="evaluate")
)
@begin_session
def complete_session(
@@ -96,7 +111,7 @@ class DBSyncSessionsHandler(DBBaseHandler):
)
.execution_options(synchronize_session="evaluate")
)
return session.query(SyncSession).filter_by(id=session_id).one()
return session.scalar(select(SyncSession).filter_by(id=session_id))
@begin_session
def fail_session(
@@ -115,7 +130,7 @@ class DBSyncSessionsHandler(DBBaseHandler):
)
.execution_options(synchronize_session="evaluate")
)
return session.query(SyncSession).filter_by(id=session_id).one()
return session.scalar(select(SyncSession).filter_by(id=session_id))
@begin_session
def cancel_active_sessions(