chick 9ea8021cce feat(fleet): card-only UI with progressive load and restart ops
Drop the redundant advanced table so fleet stays resource-first cards.
Keep multi-select batch service/system restart via prepare→execute, card
restarts, overview system ops, and progressive fleet loading.
2026-07-21 22:56:55 +08:00

Multi SimAdmin

一个用于聚合管理多个 SimAdmin 实例的轻量本地运维面板。

功能

  • 只需要在 config.json 配置多个 SimAdmin 地址。
  • Animal Island / 动森风格 Dashboard:暖米色背景、棕色文字、Ribbon 飘带标题、NookPhone 应用格、胶囊按钮与波点卡片。
  • UI 内直接新增、编辑、删除设备地址,配置实时写入本地 config.json,并提供返回首页入口。
  • 实例舰队列表、状态筛选、搜索、当前设备摘要。
  • 支持无密码实例状态读取;有密码实例可通过本地服务代登录并保持会话。
  • 统一代理 SimAdmin API,内置 API 工作台,便于读取/调试设备、SIM、网络、短信、eSIM、OTA 等接口。
  • 保留原始 SimAdmin 页面 iframe 嵌入;若目标站禁止 iframe,可一键在原站打开。
  • 敏感配置留在本地 config.json,仓库只提交 config.example.json

一键安装(macOS

curl -fsSL https://gitea.chickliu.fun/Hermes/multi-simadmin/raw/branch/main/scripts/install.sh | sh

脚本会从 Gitea 下载源码,固定使用 pnpm 11.13.0 安装依赖并构建 Web,然后启动:

  • LAN Gateway0.0.0.0:8788
  • Control-plane API127.0.0.1:8790
  • Canary Gateway8789 保持关闭

安装完成后按终端输出访问 http://<本机局域网 IP>:8788/fleet。首次密码可在当前管理台直接设置。Gateway 默认监听所有网络接口,因此必须确保主机仅接入可信内网或已通过防火墙限制 8788 的来源;HTTP 部署不得直接暴露到公网,公网开放必须由前置代理提供 HTTPS 和访问控制。

当前生产秘密存储使用 macOS Keychain,因此一键安装脚本暂只支持 macOS。脚本不会覆盖已有源码目录或数据库,也不会占用已被其他进程监听的 8788/8790 端口。

服务管理

下载脚本后可执行完整生命周期命令:

curl -fsSL https://gitea.chickliu.fun/Hermes/multi-simadmin/raw/branch/main/scripts/install.sh -o /tmp/multi-simadmin-install.sh
sh /tmp/multi-simadmin-install.sh status
sh /tmp/multi-simadmin-install.sh restart
sh /tmp/multi-simadmin-install.sh stop
sh /tmp/multi-simadmin-install.sh start
sh /tmp/multi-simadmin-install.sh uninstall

uninstall 默认只移除程序源码,保留数据库、Gateway token 和日志。默认安装位置为 ~/Library/Application Support/multi-simadmin,可通过 MULTI_SIMADMIN_HOME 覆盖;额外 LAN Host 可通过 MULTI_SIMADMIN_ALLOWED_HOSTS(逗号分隔)配置。

旧版开发入口

server/public/ 仅用于历史兼容和开发验证,不是当前生产安装入口:

cp config.example.json config.json
npm install
npm start

配置

编辑 config.json

{
  "server": { "host": "127.0.0.1", "port": 8788 },
  "instances": [
    {
      "id": "simadmin-open",
      "name": "无密码 SimAdmin",
      "url": "http://192.168.68.1:3000",
      "description": "密码保护关闭或尚未配置密码的实例",
      "auth": { "mode": "none" },
      "tags": ["open"]
    },
    {
      "id": "simadmin-password",
      "name": "有密码 SimAdmin",
      "url": "http://192.168.68.2:3000",
      "description": "服务端代登录并保存会话 Cookie;密码只保存在本地 config.json",
      "auth": {
        "mode": "password",
        "password": "CHANGE_ME"
      },
      "tags": ["password"]
    }
  ]
}

字段说明:

  • id:唯一标识,只能包含字母、数字、下划线、短横线、点号。
  • name:页面显示名称。
  • urlSimAdmin 原始访问地址。
  • description:可选描述。
  • auth.modenone 表示无密码;password 表示由本地聚合服务代登录。
  • auth.password:可选。本地保存后可自动刷新会话;不要提交真实密码。

为什么只接受 IP 地址?

聚合服务会代理设备管理 API。为避免 DNS 重绑定将已校验主机名切换到本机或元数据地址,设备 URL 必须使用明确的 IPv4/IPv6 字面地址;局域网 IP(如 192.168.x.x)可正常使用。

常用命令

npm test
npm start

架构与阶段边界

第一阶段采用无构建链的可组合架构:

  • server/index.js 是唯一 bootstrap,负责路径、监听和优雅关闭;server/app.jsbuildApp() 无导入副作用,可通过 Fastify inject() 测试。
  • server/config/ 提供 immutable snapshot、串行事务和 temp + atomic rename;模板只用于首次加载,提交始终写实际 config.json
  • server/clients/ 在配置成功提交后 reconcile;server/status/server/proxy/ 是独立边界。
  • public/domaininfrastructurestaterouter 放置 Node 可测试模块,app.js 暂作 DOM composition/controller。
  • styles.css 依次导入 tokens、base、components、responsive。

验证命令:

npm test
find server public test -name '*.js' -exec node --check {} \;
npm audit --omit=dev --audit-level=high
git diff --check

第二阶段安全加固后,服务仅允许监听 loopback;显式配置 0.0.0.0、LAN 地址或非 loopback 主机将拒绝启动。实例目标必须为允许的 IP 字面地址,拒绝凭据、loopback、unspecified、link-local、metadata、multicast 及主机名,以避免 DNS 重绑定。生产代理使用精确路径/方法白名单,写操作要求短期一次性确认令牌。

注意

  • 当前项目是本地管理面板,不修改 SimAdmin 本体。
  • config.json 可能包含真实设备地址和密码,已在 .gitignore 中排除。
  • 部分写操作接口(短信发送、网络模式、频段/小区锁定等)会改变设备状态,执行前请确认目标实例。
S
Description
Config-driven dashboard to manage multiple SimAdmin instances from one page
Readme
5.2 MiB
Languages
TypeScript 88.3%
JavaScript 6.5%
CSS 4.3%
HTML 0.5%
Shell 0.4%