Files
Nexus/docs/research/jumpserver-easynode-architecture-analysis.md
T
Your Name 3ecd3d75d4 docs: reports + research + team roster + tech-stack evaluation
Add implementation progress reports, review reports (CTO/quality/deploy),
session log, signoff table, team roster, collaboration charter,
tech-stack evaluation and inventory, research reports.
Remove obsolete multisync_server.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:12:29 +08:00

29 KiB
Raw Blame History

JumpServer & EasyNode 深度架构分析 — Nexus 技术参考报告

分析日期:2026-05-21 | 分析师:软件架构师 | 状态:ADRs 提议中


1. 执行摘要

分析范围jumpserver/jumpserver、jumpserver/koko、jumpserver/luna、jumpserver/lion、chaos-zhu/easynode

对 Nexus 的核心参考价值排序:

排名 项目 技术栈 Stars 最大参考价值 直接可复用度
1 JumpServer Core Python + Django 30.5k 资产 Platform 模型、RBAC 授权链路、会话审计存储方案 设计思路完全可复用,代码需 Django→FastAPI 迁移
2 Koko Go + Vue 531 SSH 连接池、WebSocket-TTY 代理、asciicast 录像、Redis 房间模式 Go→Python 重写,架构模式可直接借鉴
3 EasyNode Vue + Node.js 2k Web SSH 数据流、批量指令并行队列、凭据加密存储、脚本库设计 架构简洁适合小规模参考,Node.js→Python 重写
4 Luna Angular + TypeScript 303 xterm.js 集成方式、多标签终端管理、文件管理器 UI 直接复用前端方案
5 Lion Go + Vue 22 RDP/VNC Guacamole 连接器 仅当 Nexus 需要图形协议时参考

2. 资产模型对比分析

2.1 JumpServer 资产模型架构

                         ┌──────────────────┐
                         │    Platform      │  (模板层)
                         │  - name (唯一)    │
                         │  - category      │  host/device/database/cloud/web/gpt/custom
                         │  - type          │  linux/windows/mysql/k8s...
                         │  - charset       │
                         │  - protocols ────┼──▶ PlatformProtocol (1:N)
                         │  - automation ───┼──▶ PlatformAutomation (1:1)
                         └────────┬─────────┘
                                  │ FK (PROTECT)
                                  ▼
              ┌──────────────────────────────────────┐
              │              Asset                   │  (实例层 - 多表继承)
              │  - name, address                     │
              │  - platform (FK)                     │
              │  - nodes (M2M → Node)                │
              │  - zone (FK → Zone, 可选)            │
              │  - connectivity                      │
              │  - gathered_info (JSON)              │
              │  ┌──────┬──────┬──────┬──────┐      │
              │  │ Host │Device│Database│Cloud│ ...  │  (子类特化)
              │  └──────┴──────┴──────┴──────┘      │
              └──────────────────────────────────────┘
                                  │ M2M
                                  ▼
              ┌──────────────────────────────────────┐
              │              Node                    │  (组织层 - 键位树)
              │  - key: "1:2:3"  (冒号分隔层级)      │
              │  - value: "WebServers"               │
              │  - full_value: "/Default/Prod/Web"   │
              │  - child_mark (子节点计数器)          │
              │  - assets_amount (缓存)               │
              └──────────────────────────────────────┘

2.2 核心设计决策解读

Platform = 资产模板/类型定义

  • 不在 Asset 表直接存储 categorytype,而是通过 platform FK 派生
  • 查询时用 F() 表达式优化:queryset.annotate(category=F("platform__category"))
  • 优点:修改资产分类只需换平台引用,无需改 Asset 记录

Node = 键位树(Key-Based Tree

  • 不存 parent_id 自引用,而是用 key 字符串编码层级:"1:2:3"
  • 查询所有后代:Q(key__istartswith=f'{self.key}:') —— 一次查询解决
  • 查询祖先:key.split(':') 算出来,key__in=[...] 一次查询
  • 优点:避免了递归 CTE 和嵌套集(nested set)的复杂度

多表继承

  • Asset 是基表,Host, Device, Database, Cloud 等是子表
  • 每个子类存储类别独有的字段(Host 有 os/archDatabase 有 db_name/db_type

2.3 Nexus 适配建议

JumpServer 设计 Nexus 适配 改造点
Platform 模板 直接采纳 — 创建 platforms 表作为资产类型模板 Django FK → SQLAlchemy relationship + ForeignKey
Node 键位树 采纳但简化 — 2000+ 服务器规模用 parent_id 自引用更直观,配合 PostgreSQL ltree 扩展 键位树在 SQLAlchemy 中无原生优势
Asset 多表继承 改为单表+JSON — 用 extra_attrs JSONB 替代多表继承 Django 多表继承 → SQLAlchemy 单表 + JSONB
Protocol 实例级覆盖 采纳 — 允许每个 Asset 覆盖默认端口 直接复用设计
Node 缓存映射 采纳但用 Redis{node_id: [asset_ids]} 的 Redis Hash 缓存 Django 三级缓存 → 直接用 Redis

2.4 推荐的 Nexus Asset 模型

# SQLAlchemy Async 模型
class Platform(Base):
    __tablename__ = "platforms"
    id: int (PK)
    name: str (unique)           # "Linux", "Windows", "MySQL"
    category: str                # "host", "database", "device"
    type: str                    # "linux", "mysql", "switch"
    default_protocols: JSONB     # [{"name":"ssh","port":22,"primary":true}]

class Asset(Base):
    __tablename__ = "assets"
    id: UUID (PK)
    name: str
    address: str                 # IP 或域名
    platform_id: FK  Platform
    node_id: FK  Node (可选)
    tags: M2M  Tag
    protocols: JSONB            # 实例级覆盖: [{"name":"ssh","port":2222}]
    extra_attrs: JSONB          # 替代多表继承: {"os":"ubuntu","arch":"x86_64",...}
    connectivity: str           # "ok","err","unknown"
    last_checked_at: datetime

class Node(Base):
    __tablename__ = "nodes"
    id: UUID (PK)
    name: str
    parent_id: FK  Node (nullable)  # 自引用树
    # 或直接用 PostgreSQL ltree:
    # path: ltree                  # "root.production.web"

3. 权限体系对比分析

3.1 JumpServer 四层权限模型

Layer 1: 认证 (Authentication)
  └── 密码/MFA/LDAP/CAS/OIDC/SAML2/OAuth2/RADIUS

Layer 2: RBAC (角色权限 — 控制管理界面操作)
  └── Role → RoleBinding → User
      ├── SystemAdmin / SystemAuditor / SystemUser (全局)
      └── OrgAdmin / OrgAuditor / OrgUser            (组织级)

Layer 3: Asset Permission (资产授权 — 谁能访问什么)
  └── AssetPermission {
        users: M2M[User]         ← 授权给谁
        user_groups: M2M[Group]
        assets: M2M[Asset]       ← 授权哪些资产
        nodes: M2M[Node]         ← 授权哪些节点(子节点自动继承)
        accounts: JSON[account_ids / "@ALL" / "@USER" / "@INPUT"]
        protocols: JSON          ← 允许的协议
        actions: int             ← connect/upload/download/clipboard
        date_start / date_expired ← 时间窗口
        is_active: bool          ← 启用/停用
      }

Layer 4: ACL (访问控制列表 — 精细化控制)
  ├── LoginAssetACL       → accept/reject/review/notice/face_verify
  ├── CommandFilterACL    → allow/deny/review (正则匹配危险命令)
  ├── ConnectMethodACL    → 控制 WebCLI/SSH/RDP 等连接方式
  └── DataMaskingRule     → 数据库查询脱敏

3.2 资产授权链核心查询(JumpServer 实现)

# AssetPermission.get_all_assets() — 计算用户可访问的完整资产集
def get_all_assets(self):
    # Step 1: 直接授权的资产
    asset_ids = set(self.assets.all().values_list('id', flat=True))
    # Step 2: 通过节点授权的资产(含后代节点)
    nodes_keys = self.nodes.all().values_list('key', flat=True)
    nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys)
    asset_ids.update(nodes_asset_ids)
    return Asset.objects.filter(id__in=asset_ids)

3.3 Nexus 适配建议

JumpServer 设计 Nexus 适配 说明
四层权限模型 简化为三层:认证 → RBAC → 资产授权 Nexus 初期不需要 ACL 层的全部复杂度
AssetPermission 模型 直接采纳核心字段 users/groups/assets/nodes/accounts/date_range/is_active
命令过滤 后期实现,初期提供命令日志审计 使用正则匹配,会话中实时拦截
账号别名(@ALL/@USER/@INPUT) 采纳 JumpServer 的精妙设计
时间窗口授权 采纳 date_start/date_expired,简单有效

3.4 推荐的 Nexus 权限模型

class AssetPermission(Base):
    __tablename__ = "asset_permissions"
    id: UUID (PK)
    name: str
    users: M2M[User]
    user_groups: M2M[UserGroup]
    assets: M2M[Asset]
    nodes: M2M[Node]
    accounts: JSONB            # ["root", "@ALL", "@USER"]
    actions: JSONB             # ["connect", "upload", "download"]
    date_start: datetime
    date_expired: datetime
    is_active: bool

4. 组件架构对比分析 (Core + Connector 模式)

4.1 JumpServer 组件通信架构

                          ┌──────────────────────┐
                          │    Nginx / LB         │
                          └───────┬──────────────┘
                                  │
          ┌───────────────────────┼───────────────────────┐
          │                       │                       │
          ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Core (Django) │    │  Koko (Go)      │    │  Lion (Go)      │
│   :8080 (REST)  │    │  :2222 (SSH)    │    │  :3389 (RDP)    │
│   :8070 (WS)    │    │  :5000 (WS)     │    │  :8081 (WS)     │
└────────┬────────┘    └────────┬────────┘    └────────┬────────┘
         │                      │                      │
         │     Redis Pub/Sub    │                      │
         ├──────────────────────┼──────────────────────┤
         │  DB 0: Session Rooms │                      │
         │  DB 4: Asset Cache   │                      │
         │  DB 6: WS Messages   │                      │
         │                      │                      │
         ▼                      ▼                      ▼
┌─────────────────┐    ┌──────────────────────┐
│  MySQL/Postgres │    │  Target Assets       │
└─────────────────┘    └──────────────────────┘

4.2 Bootstrap Token 注册流程

Koko 启动
  │
  ├─ 1. POST /api/v1/terminal/terminal-registrations/
  │      Header: Authorization: BootstrapToken <TOKEN>
  │      Body: {"name": "Koko-01", "type": "koko", ...}
  │
  ▼
Core 验证 Bootstrap Token (与 config.txt 比对)
  │
  ├─ 2. 生成 Access Key (ID + Secret)
  │      保存到 Core DB (terminal_terminalregistration 表)
  │      返回 {id, key, secret}
  │
  ▼
Koko 保存 Access Key 到本地文件 (data/keys/.access_key)
  │
  ├─ 3. 后续所有 API 调用使用 Access Key
  │      Header: Authorization: Key <ACCESS_KEY_ID>:<SIGNATURE>
  │
  ├─ 4. 心跳保活 (每 30s)
  │      POST /api/v1/terminal/terminal-registrations/<id>/heartbeat/
  │
  ▼
稳定运行

4.3 用户连接完整流程

用户(Browser) --WS--> Luna --WS--> Koko(:5000) --REST--> Core(:8080) --Redis/DB
                       │                                    │
                       │  1. WebSocket Connect               │
                       │  2. TerminalInit(token)             │
                       │                                     │
                       │  3. Koko 验证 ConnectToken ─────────┤
                       │  4. Core 返回(asset, account, cred) │
                       │                                     │
                       │  5. Koko --SSH--> Target Asset      │
                       │  6. I/O 双向流                       │
                       │  7. asciicast 实时录像              │
                       │                                     │
                       │  8. 会话结束,上传录像 ──────────────┤

4.4 Nexus 适配建议

JumpServer 设计 Nexus 适配
Connector 分离 采纳但简化 — Nexus 可将 SSH 代理内嵌为 FastAPI 子进程或 Docker sidecar,不需要独立的 Go 二进制
Bootstrap Token + Access Key 采纳 — 内部服务间认证的最佳实践
Redis Pub/Sub 房间模式 采纳 — Koko 的多实例会话共享通过 Redis RoomNexus 多 worker 同理
Connect Token 采纳 — 短生命周期的一次性连接令牌,JumpServer 最精华的安全设计

4.5 Nexus 推荐架构

Nexus API Server (FastAPI)
  ├── /api/v1/*             REST API
  ├── /ws/terminal/{token}  WebSocket 终端端点
  └── SSH Proxy Service     内置 SSH 代理 (asyncssh)
        │
        ├── 连接池管理
        ├── asciicast 录像
        └── 命令过滤拦截

5. Web SSH 方案对比

5.1 三项目数据流对比

Koko (Go) — 最成熟但最重

浏览器(Luna, xterm.js)
  │ WebSocket (JSON: {type:"TERMINAL_DATA", data:"ls\n"})
  ▼
Koko HTTPD Server (:5000)
  ├── UserWebsocket  → 解析消息类型
  ├── tty.go         → TerminalInit(connectToken) 验证
  ├── srvconn/ssh.go → 建立 SSH 连接到目标
  └── 双向 I/O 代理
       ├── 浏览器 → Koko → SSH (stdin)
       ├── SSH → Koko → 浏览器 (stdout)
       ├── asciicast 录像 (asciinema/asciinema.go)
       └── 窗口 resize: TerminalResize → ssh.Window{Width,Height}

EasyNode (Node.js) — 最简洁

浏览器(Vue, xterm.js)
  │ WebSocket (ws://host:8082/terminal?hostId=xxx)
  ▼
Koa Server
  ├── controller/terminal.js → 处理 terminal WS
  ├── controller/ssh.js      → 凭证管理 (CRUD for NeDB)
  └── ssh2 npm 库            → SSH 连接到目标
       └── SSH Stream ↔ WebSocket 双向转发

Luna (Angular) — Web Terminal UI 参考

Luna 核心组件:
  ├── pages-terminal     → 终端页面容器 (多标签)
  ├── elements-terminal  → xterm.js 包装组件
  ├── elements-replay-asciicast  → 录像回放
  ├── elements-sftp      → SFTP 文件管理 UI
  └── 通信层
       ├── WebSocket → /ws/terminal/?token=xxx
       └── HTTP REST → Core API (/api/v1/...)

5.2 终端数据流关键细节

WebSocket 消息协议 (Koko)

// 浏览器 → Koko
{"id":"uuid","type":"TERMINAL_INIT","data":"{\"cols\":80,\"rows\":24,\"code\":\"...\"}"}
{"id":"uuid","type":"TERMINAL_DATA","data":"ls -la\n"}
{"id":"uuid","type":"TERMINAL_RESIZE","data":"{\"cols\":120,\"rows\":40}"}
{"id":"uuid","type":"TERMINAL_BINARY","raw":"<base64>"}

// Koko → 浏览器
{"id":"uuid","type":"TERMINAL_SESSION","data":"{\"session\":{...}}"}
{"id":"uuid","type":"TERMINAL_DATA","data":"file\n"}
{"id":"uuid","type":"CLOSE"}

SSH 连接复用 (Koko)

// koko/pkg/srvconn/ssh.go
type SSHClient struct {
    *gossh.Client
    refCount int32              // 引用计数
    traceSessionMap map[*gossh.Session]time.Time // 活跃 session 追踪
    ProxyClient *SSHClient      // 支持跳板机代理链
}

func (s *SSHClient) AcquireSession() // refCount++ 复用连接
func (s *SSHClient) ReleaseSession() // refCount-- 还回连接池

5.3 Nexus 推荐方案

综合最优方案:Koko 协议 + EasyNode 简洁架构 + Python 生态实现

维度 建议 技术选型
WebSocket 协议 采用 Koko 的消息格式 (TERMINAL_INIT/DATA/RESIZE/CLOSE) 已验证的协议设计
SSH 后端 Python asyncssh (纯 Python, async/await 原生支持) 比 paramiko 更适合 FastAPI 异步
连接池 采用 Koko 的引用计数模式 复用 SSH 连接,减少握手开销
SFTP asyncssh 内置 SFTP 支持 无需额外库
窗口 resize WebSocket SIGWINCH → ssh.resize_pty() 标准实现
前端 xterm.js + xterm-addon-fit/weblinks/webgl 社区最成熟的 Web Terminal 方案
录像格式 asciicast v2 (.cast.gz) 与 asciinema 生态兼容

6. 会话审计方案

6.1 JumpServer 审计架构

会话生命周期:
  Session.created → Session.connected → Session.disconnected → Session.ended

录像生成:
  KoKo: SSH I/O → asciinema/asciinema.go → .cast.gz (单文件)
    或: → part.1.cast + part.2.cast + ... + replay.json (流式分片)

存储路径:
  MEDIA_ROOT/replay/{YYYY-MM-DD}/{session_id}/
    ├── {session_id}.cast.gz          # asciicast SSH 录像
    ├── {session_id}.replay.json      # 元数据清单 (v3+)
    ├── {session_id}.part.1.cast      # 流式分片
    └── {session_id}.part.N.cast

存储后端:
  Local (default_storage) → Celery 异步上传 → S3/OSS/MinIO/Ceph (SERVER_REPLAY_STORAGE)

命令日志:
  session.cmd_filter_rules_acls → CommandFilterACL 实时匹配
  session.command_amount         → 统计计数

6.2 审计相关模型

# JumpServer terminal/models/session.py (核心字段推断)
class Session:
    id: UUID
    user: FK[User]
    asset: FK[Asset]
    account: FK[Account]
    protocol: str               # "ssh", "rdp", "mysql"
    remote_addr: str            # 客户端 IP
    is_finished: bool
    date_start: datetime
    date_end: datetime
    has_replay: bool
    has_command: bool
    command_amount: int
    terminal: FK[TerminalRegistration]  # 哪个 Connector 处理的

6.3 Nexus 审计方案推荐

功能 方案
录像格式 asciicast v2 (.cast.gz) — 纯文本+gzip,存储成本低,可流式播放
录像生成 Python asciinema 兼容的输出格式,在 SSH 代理层直接生成
录制模式 流式分片 (part-based) — 每 5 分钟或 1MB 切一片,避免单文件过大
存储 本地磁盘 → 定时任务上传 S3/MinIO (异步)
回放 前端 xterm.js + asciinema-player 组件
命令日志 在 SSH stdin 流中按 \n 分割提取,存入 command_logs
实时监控 WebSocket 推送在线会话列表,支持管理员实时旁观 (Room 模式)
保留策略 按天数自动过期删除 (配置化)

7. 架构决策建议 (ADR 格式)

ADR-001: 资产模型采用 Platform 模板 + 单表 Asset + JSONB 扩展属性

状态: 提议

上下文: Nexus 管理 2000+ 服务器,覆盖 Linux/Windows/网络设备/数据库。JumpServer 使用 Django 多表继承实现分类,但这对 SQLAlchemy + FastAPI 过于复杂。

决策: 采用 JumpServer 的 Platform 概念(资产类型模板),但用单表 Asset + extra_attrs JSONB 替代多表继承。

后果:

  • 优点:查询简单,JSONB 索引支持高效过滤,新增资产类型无需改表
  • 缺点:无法在数据库层面约束特定类型的必填字段
  • 缓解:Pydantic 验证层做类型特定的字段校验

ADR-002: Web SSH 采用 BFF (Backend For Frontend) 模式,内嵌 SSH 代理

状态: 提议

上下文: JumpServer 将 Core API 和 Koko 分离为不同进程(Django + Go)。这种分离增加了部署复杂度。Nexus 初期不需要支持多协议(RDP/VNC)。

决策: Nexus 将 SSH 代理功能内嵌在 FastAPI 进程中:

  • WebSocket 端点 /ws/terminal/{token} 直接在 FastAPI 中处理
  • 使用 asyncssh 库建立 SSH 连接
  • 连接池管理通过 Redis 共享给所有 worker(支持多进程部署)
  • Connect Token 机制照搬 JumpServer 的设计

后果:

  • 优点:部署简化(一个服务),开发效率高(全 Python)
  • 缺点:SSH 代理负载与 API 负载共用资源,大规模场景需要独立部署
  • 缓解:架构预留分离接口,未来可拆分为独立 SSH Proxy 服务

ADR-003: 会话录像采用 asciicast v2 格式 + 流式分片上传

状态: 提议

上下文: JumpServer 的 asciicast 格式生态成熟。EasyNode 没有录像功能。

决策:

  • 终端 I/O 按 asciicast v2 格式记录(每行 JSON: [time, type, data]
  • 每 5 分钟或 1MB 生成一个 part 文件
  • 会话结束后合并为 .cast.gz
  • 异步上传到 MinIO/S3

后果:

  • 优点:与 asciinema 生态兼容,纯文本可搜索,gzip 压缩率高
  • 缺点:不支持 RDP/VNC 图形协议录像(Nexus 不涉及)

ADR-004: 权限体系采用 RBAC + AssetPermission 两层

状态: 提议

上下文: JumpServer 四层(认证+RBAC+AssetPermission+ACL)对 Nexus 初期过度设计。

决策:

  • Layer 1: JWT 认证 + MFA (TOTP)
  • Layer 2: RBAC 角色 (admin/operator/auditor/user)
  • Layer 3: AssetPermission (用户/用户组 → 资产/节点 + 账号 + 时间窗口 + 动作)

ACL 层(命令过滤、连接方式控制)推迟到 v2。

后果:

  • 优点:实现简单,2000+ 服务器规模够用
  • 缺点:缺少命令级精细化控制
  • 缓解:v1 提供命令日志审计(事后追溯),v2 加上实时命令过滤

ADR-005: 前端终端方案采用 xterm.js + WebSocket 自定义协议

状态: 提议

上下文: Luna 使用 Angular + xterm.jsEasyNode 使用 Vue + xterm.js。两个项目的 xterm.js 集成模式类似。

决策:

  • 前端:纯 HTML/Tailwind CSS v4 + 原生 JS 的 xterm.js(不引入 Vue/Angular 框架,与 Nexus 现有技术栈一致)
  • WebSocket 消息格式:采用 Koko 的消息类型定义(TERMINAL_INIT/DATA/RESIZE/CLOSE/SFTP_*
  • 多标签页:轻量级标签管理器

后果:

  • 优点:零额外框架依赖,体积小,性能好
  • 缺点:需要自行实现终端标签管理、会话恢复等 UI 逻辑

8. 技术迁移评估:Django → FastAPI

8.1 关键差异对照表

维度 Django / DRF FastAPI 迁移难度
ORM Django ORM (同步, lazy queryset) SQLAlchemy 2.0 Async (显式 async, eager loading)
序列化器 DRF Serializer (声明式) Pydantic v2 (类型注解)
路由 Django URL conf + ViewSet FastAPI APIRouter + path decorators
中间件 Django Middleware FastAPI Middleware / Depends
权限 Django Permission + DRF Permission FastAPI Depends + 自定义权限函数
分页 DRF PageNumberPagination fastapi-pagination 或自行实现
过滤 django-filter + FilterSet SQLAlchemy 查询参数 + fastapi-filter
信号 Django Signals SQLAlchemy Events
Celery Django-Celery Celery (独立)
WebSocket Django Channels FastAPI 原生 WebSocket
组织隔离 current_org 线程局部变量 ContextVar 或中间件注入

8.2 关键迁移模式

Django ORM → SQLAlchemy Async 查询等价写法

# Django: 分页 + 过滤 + 预加载
assets = Asset.objects.filter(
    platform__name="Linux", is_active=True
).select_related("platform").prefetch_related("nodes").order_by("name")
paginator = PageNumberPagination()
page = paginator.paginate_queryset(assets, request)

# SQLAlchemy Async: 等价写法
from sqlalchemy import select
from sqlalchemy.orm import selectinload

stmt = (
    select(Asset)
    .options(
        selectinload(Asset.platform),
        selectinload(Asset.nodes),
    )
    .where(Asset.platform.has(name="Linux"), Asset.is_active == True)
    .order_by(Asset.name)
)
page = await paginate(session, stmt, offset=offset, limit=limit)

DRF ViewSet → FastAPI Router

# Django DRF
class AssetViewSet(OrgBulkModelViewSet):
    model = Asset
    filterset_class = AssetFilterSet
    search_fields = ("name", "address", "comment")

    @action(methods=["GET"], detail=True, url_path="platform")
    def platform(self, request, pk=None): ...

# FastAPI
router = APIRouter(prefix="/api/v1/assets/assets", tags=["assets"])

@router.get("/")
async def list_assets(
    search: str = Query(None),
    platform: str = Query(None),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
): ...

@router.get("/{asset_id}/platform")
async def get_asset_platform(
    asset_id: UUID,
    db: AsyncSession = Depends(get_db),
): ...

Django Signals → SQLAlchemy Events

# Django
@receiver(post_save, sender=Session)
def on_session_end(sender, instance, **kwargs):
    if instance.is_finished:
        upload_replay_to_s3.delay(instance.id)

# SQLAlchemy Async
@event.listens_for(Session, "after_update")
def on_session_update(mapper, connection, target):
    if target.is_finished:
        # 注意: async event 需要特殊处理
        asyncio.ensure_future(upload_replay(target.id))

8.3 Django ORM ↔ SQLAlchemy 2.0 常用查询对照

Django ORM SQLAlchemy 2.0 Async
qs.filter(Q(a=1) | Q(b=2)) where(or_(Model.a==1, Model.b==2))
qs.annotate(cnt=Count('assets')) select(Model).options() + 子查询或 column_property
qs.values_list('id', flat=True) select(Model.id) + scalars().all()
qs.select_related('platform') .options(selectinload(Model.platform))
qs.prefetch_related('nodes') .options(selectinload(Model.nodes))
qs.bulk_create(objs) session.add_all(objs) 配合 server-side cursor
qs.bulk_update(objs, ['name']) session.execute(update(Model), [...])
qs.get_or_create() 手动 select → insert/return
Atomic(transaction) async with session.begin():
Node.get_all_children() (key istartswith) where(Node.path.like(f'{parent_path}.%'))

8.4 迁移风险与注意事项

  1. 查询性能Django ORM 的 lazy loading 在 SQLAlchemy Async 中必须显式声明为 selectinloadjoinedload,否则 N+1 问题立现
  2. 事务管理Django 的 ATOMIC_REQUESTS 自动包裹请求,FastAPI 需要显式管理 async with session.begin()
  3. 多租户过滤JumpServer 的 current_org 线程局部变量在 async 环境中不安全,必须用 ContextVar 或依赖注入
  4. WebSocket 中访问数据库Django Channels 支持同步 ORMFastAPI WebSocket 中需用 AsyncSession
  5. Pydantic 验证层CRUD 场景下,需要在 Pydantic schema 和 SQLAlchemy model 间建立清晰的转换层(create_dto / update_dto / response_dto

9. 建议的 Nexus 实现优先级

优先级 模块 参考项目 工作量估计
P0 资产 CRUD + Platform 模板 + Node 树 JumpServer 2-3 周
P0 JWT 认证 + MFA JumpServer 1 周
P0 Web SSH 终端 (WebSocket + asyncssh + xterm.js) Koko + EasyNode + Luna 3-4 周
P1 RBAC + AssetPermission 授权 JumpServer 2 周
P1 SFTP 文件管理 (Web 界面) Luna + EasyNode 1-2 周
P1 会话录像 (asciicast) + 存储 JumpServer 2 周
P2 批量指令下发 EasyNode 1-2 周
P2 命令日志 + 审计 JumpServer 1 周
P2 凭据托管 (加密存储) EasyNode 1 周
P3 脚本库 EasyNode 1 周
P3 命令过滤 ACL JumpServer 2 周

参考来源