# ruying **Repository Path**: f52d/ruying ## Basic Information - **Project Name**: ruying - **Description**: 如影 ruying —— 本地与服务器之间的文件同步工具,支持只上传/只下载/双向同步,冲突自动归档到镜像目录。 - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-22 - **Last Updated**: 2026-10-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 如影 ruying > 本地与服务器之间的文件同步,像影子一样跟着你。 > Windows / macOS / Linux 桌面工具 + 无界面守护进程,AGPL-3.0 开源。 ## 下载 | 文件 | 说明 | |---|---| | [ruying.exe](https://gitee.com/f52d/ruying/releases/download/v0.3.11-beta/ruying.exe) | Windows 版(当前是测试版),约 6.1 MB,免安装。一个文件两用:双击=托盘程序,带参数=命令行 | 最新发行版: | 软件主页: 双击 `ruying.exe` → 右下角托盘出现标志(**不会弹窗**)→ 左键点它,面板才在图标旁边弹出;再点一下图标、或点面板外面的空白处就收起。没有安装包、没有解压步骤,也不需要管理员权限。**已经在跑时再双击,不会开浏览器**,只是把托盘面板重新弹出来;想看浏览器版面板用 `ruying open`。 **它跑在哪里**:第一次双击时,如影会把自己复制到 `%APPDATA%\ruying\ruying.exe`,以后常驻的就是这一份。你从下载目录、桌面、U 盘再双击,都只是把新版带过来、然后交棒给那一份——所以开机自启和自动更新永远只认一个位置,不会出现三个副本各守一份索引。想换回去也简单:删掉 `%APPDATA%\ruying` 里的 `ruying.exe` 即可(配置、索引、归档都在同一个目录,删之前先备份)。 **开机自启与自动更新**:默认都开着。开机自启写的是当前用户的注册表启动项(任务管理器 → 启动 里能看到「ruying」),托盘菜单「开机自启:已开启」点一下就关。自动更新每 4 小时查一次 Gitee 上的版本号,查到新版就下载、原地换掉 `%APPDATA%\ruying\ruying.exe` 再重启;换之前会先让引擎正常退出、把索引落盘。面板底部「设置」里可以关掉自动更新、在正式版和测试版之间切换通道,也可以手动点「检查更新」。命令行等价:`ruying settings`、`ruying update`。 ## 界面长什么样 **托盘浮窗(400×620)=同步监视器**,像 OneDrive 那样一打开就是一张文件表,没有大标题、也没有“正在快速比对”这类说明句: ```text ● 1 个正在同步 · 共 5 个任务 [文件] ↑ 上线包/dist/app.js 正在传 [文件] ⚠ 上线包/readme.md 待你定 [文件] ↑ 上线包/dist/base.css 10 秒前 [文件] ↑ 上线包/vendor/x.php 失败 13 秒前 任务 [docs 2 项待确认 ▾] ← 点一下就地定,不用去别处找 ``` - 最左边是文件类型图标(代码 / 数据 / 图片 / 压缩包 / 文档…),紧跟一个动作图标:↑ 上传、↓ 下载、⇄ 改名、⌫ 删除、⚠ 冲突。不再写“上传”“下载”四个字,横向空间让给文件名 - 目录只留最后一级、空间不够时先挤掉它;**文件名和后缀始终留全**。完整相对路径、电脑上的实际位置、服务器地址、大小、精确时间、失败原因全在悬停提示里 - **托盘图标会跟着同步动**:正在传文件时右下角挂蓝色角标,里面写着「还剩几个没传完」,外面套一圈会转的白弧;传完自己摘掉。服务器连不上、或者任务没在跑时挂红色感叹号;有事项要你定但没出错时挂橙色角标,数字就是待确认条数。鼠标停上去、点开托盘菜单第一行,都能看到「正在同步 3/12 · 21 项待确认 · 5 个任务」这样一句话 - **正在传的文件一开始就进表**,状态列写着“正在传 / 排队中 / 待你定 / 失败”,传完当场变成“已完成 + 时间”,不用等整轮结束才出现 - 标记一个任务时任务名当目录前缀(`上线包/dist/app.js`),不多占一列 **大窗(浏览器或从浮窗点卡片弹出的详情窗,固定开在屏幕右下角)=左右两栏**:左边点任务,右边**先看这个任务的传输记录**——同样是那张表,正在传 / 排队 / 已完成混在一起按时间倒序;配置、预演、账目这些收进「详情」页签。传输记录会落盘,关掉面板、重启软件之后还在。 **「版本备份」页说的是「哪一版没留下、存在哪、为什么」**:每条先写一行**为什么是这一版**——两边都改过时写「服务器那份晚 12 分钟,按任务设置『哪边改得新就留哪边』留下了服务器那一版」,任务固定用某侧、你手动选的、删除同步的,也都各有一句;旧版软件留下的备份没存判断依据,就照实写出任务现在设的是哪条策略,并标明「旧记录没存两边时间」。按钮写成「服务器那一版 → 电脑」,**箭头指的就是实际动作**:把备份写回你电脑上的原位置,服务器不会当场被改,下一轮同步(方向允许时)才推上去;备份来自电脑时写「电脑旧版 → 电脑」,不会和「电脑现在这一版」混淆。写回前当前那一版会再存一份备份,点错了能退回来。页面顶部可以选择只看「需要处理」的那些;**你自己删掉的文件,删之前那一份收在页尾「已删除的文件」里,默认收起**,标「删除前留的副本」,想找回来点「这个文件 → 电脑」,不需要就放着、完全不用管。 **待确认不再让人猜**:每条写清「哪个文件、为什么该你定」,并把**这台电脑**和**服务器**的现状并排摆出来(有没有、多大、多久前改过,哪份更新还带个「较新」)。决定做成「做什么 + 会发生什么」两行: - 用电脑上的这一版 —— 服务器上那份会被替换(旧版自动备份) - 用服务器上的这一版 —— 电脑上这份会被替换(旧版自动备份) - 两边各留一份 —— 这条不再提醒,两份都留着 一边已经删掉的文件,只给做得到的选项(例如「把电脑上的这一版传回服务器 / 电脑上也删掉 / 先不动」),不会再出现点了必然失败的按钮。同一次有 3 条以上时,还会给一对「全部以电脑上的为准 / 全部以服务器上的为准」,省得一条条点 21 次;批量里每一条照样逐条走同一个接口、照样各自留备份。选「两边各留一份」时一个文件都不动,只把这个决定记下来;任何一侧之后再改一次,冲突会自己重新冒出来,不会被永久静音。 **内容一样就不叫冲突**:两边都改过时会真去读文件内容比对,一模一样就自动收敛,既不传也不来问你。只有内容确实不同才进待确认。 **保存文件就传这一个文件**:系统文件监听报出哪个路径变了,引擎只对这一个路径做三方比较,不再为了改一个文件扫整站。改名是真正的 rename(对端一次改名),不会拆成“删旧文件 + 传新文件”两步,也不会在“只备份不删”策略下留下旧路径的待确认。 **没改动时不空转**:本地每 `poll_ms`(默认 2 秒,可调)只读一遍本地目录做兜底,防止 OneDrive 漏事件;服务器那边没有推送,所以按方向定时全量——只上传默认 1 小时一次,双向/只下载 60 秒一次。全量只负责发现服务器上的改动、删除和整目录改名,不再每 2 秒扫一遍。 **断了会说出来,不会假装在同步**:远端每次读写最多等 `schedule.io_timeout_ms`(默认 30 秒),超时就重连重试,而不是无限期挂在一次网络读上。一轮里 5 分钟没有任何进展、或者引擎心跳停了 5 分钟,概览、任务角标和托盘都会变红并写明「卡住 N 分钟」,卡片上直接告诉你下一步——不会再出现界面一直显示「对账中」、其实早就没在跑的情况。 **第一次同步要你点头**:新建任务不会自动跑第一次全量。面板先做一次只读预检,告诉你本地和服务器各有几个文件;如果是「只上传」且本地是空的、服务器上已有内容,会明确提醒先「从服务器拉回」,避免以为已经在同步、其实服务器内容一直没下来。拉回是一次性的反向下载,不改变任务方向。 **默认策略是「哪边新用哪边」**:新建任务时「冲突时按保存时间自动同步」默认勾选,旧的整份进备份;想人工定就把它关掉。 ## 它解决什么问题 做网站和后端的人都有同一个痛点:**改完文件要传到服务器**。现有工具各有不顺手的地方—— - FTP/SFTP 客户端的“保持目录最新”:长时间挂着一个连接,目录一大就卡死,断了不会自己恢复; - 通用同步盘:双向同步会把服务器运行时目录(缓存、日志、用户上传)灌回本地; - 命令行 rsync:可靠,但没有状态、没有界面、没有“改了什么、冲突了怎么办”的答案。 如影的定位是**开发者向的部署/同步工具**:传输交给成熟的 rsync 或内置 SFTP,产品价值放在方向策略、冲突处理和看得见的状态上。 ## 三种模式 | 模式 | 行为 | 典型场景 | |---|---|---| | Deploy 只上传 | 本地推到远端;远端被改动只报告,不拉回 | 网站/代码发布 | | Backup 只下载 | 远端拉到本地;本地改动不外发 | 取文件、镜像备份 | | Sync 双向 | 两边都同步,冲突按策略处理并归档 | 多台机器同一份工程 | ## 安全设计(这是重点) 1. **三方比较**:本地现状 / 远端现状 / 基线索引。没有基线就分不清“谁改了”和“还没传”,也就没有可靠的双向同步。 2. **冲突不就地留副本**:落败方整份归档到 `conflicts/<事件时间>/<和源文件完全相同的相对路径>`,并写一份 `manifest.json`。项目目录永远干净。 3. **删除永远不静默**:默认只报告并保留另一侧;显式开启“删除同步”后,也是**先归档再删**,误删可从归档目录取回。界面上人工定冲突同样遵守这条:要覆盖或删除哪一侧,先把那一侧整份归档,结果里回写留底事件号。 4. **原子写入**:所有落地都先写临时文件再改名,线上不会读到半个文件。 5. **秒级时间戳统一**:NTFS 的 100ns 精度和 SFTP 的秒精度不统一会造成“永远在改动”,这里一律截断到秒;也可切换成内容哈希判等。 6. **口令不进配置文件**:只支持密钥或环境变量。 ## 快速开始 ```bash ruying init # 生成一个可直接跑通的示例任务(%DATA%/profiles/demo.json) ruying plan demo # 只比较,不动任何文件 ruying run demo # 新任务先手动跑一次首次同步;跑过之后才进入自动调度 ruying conflicts demo # 列出冲突与删除归档 ruying restore demo --event 20261022-094705-01 --side remote # 把归档那一版放回去 ruying fix demo --rel readme.md --choice both # 定一条待确认:both / local / remote / del-local / del-remote ruying start # 常驻托管:跑过的任务自动同步,面板 http://127.0.0.1:49880/ ruying status # 命令行看状态(含待确认的文件名和一条能照抄的 fix 命令) ruying sync demo | pause | resume | stop ruying profiles | enable demo | disable demo | remove demo ruying settings # 看/改开机自启、自动更新、更新通道 ruying autostart off # 不想开机就常驻:单独关自启 ruying update # 查一次新版;ruying update --apply 立刻换上并重启 ``` ``` 任务配置放在 `%DATA%/profiles/*.json`,一个文件一个任务。改文件即改任务,运行中的进程每 5 秒热加载一次,不用重启。 一个真实的部署 profile: ```json { "name": "site", "local_root": "D:/work/mysite", "transport": { "kind": "sftp", "host": "example.com", "port": 22, "user": "deploy", "key_path": "C:/Users/me/.ssh/id_ed25519" }, "remote_root": "/srv/www", "direction": "deploy", "conflict_policy": "newest_wins", "delete_policy": "archive_only", "enabled": true, "rules": { "exclude": ["node_modules/", ".git/", "data/cache/", "*.tmp", "~$*"], "include": [] }, "conflict_dir": "%DATA%/conflicts/site", "index_file": "%DATA%/index/site.json" } ``` 写起来更省心的几点: - `conflict_dir`、`index_file` 可以整段省略,自动落到 `%DATA%/conflicts/<任务名>` 和 `%DATA%/index/<任务名>.json` - 任何路径里都能写 `%DATA%`,展开成用户数据目录(Windows `%APPDATA% uying`,Linux `$XDG_DATA_HOME/ruying`),配置拷到别的机器也不用改 - 枚举值统一小写下划线(`deploy` / `newest_wins`),旧的大驼峰写法照样能读,不至于改了写法就打不开老配置 - `schedule.poll_ms` 是本地兜底扫描间隔,默认 2000ms;只读本地目录,不连服务器 - `schedule.remote_poll_ms` 是远端全量间隔;0(默认)表示按方向自动:只上传 1 小时、双向/只下载 60 秒。连不上时按 `retry_delays_ms` 退避,不会把 CPU 和日志刷满 数据目录的固定布局: ```text /profiles/<任务名>.json 任务配置(唯一真源) /index/<任务名>.json 基线索引 /conflicts/<任务名>/ 冲突与删除归档 + manifest.json /history/<任务名>.jsonl 传输记录(最近两千条,关掉界面也不丢) /daemon.token 本机控制接口令牌 /status.json 最近一轮聚合状态(给外部脚本/监控读) ``` ### 忽略规则语义 - 以 `/` 结尾:目录规则,命中该目录及其下所有内容,例如 `data/cache/` - 不含 `/`:按名字命中任意路径段,例如 `*.tmp`、`node_modules` - 含 `/`:整条相对路径通配,`**` 跨目录、`*` 不跨目录、`?` 单字符,例如 `doc/**/*.md` - `include` 优先于 `exclude`,用来从大目录规则里挖洞 ## 平台支持 | 平台 | 状态 | |---|---| | Windows 10/11 | 主要开发平台 | | macOS / Linux | 引擎纯 Rust,跨平台是设计前提;桌面壳基于 Tauri | | 国产 Linux(UOS、麒麟) | 走系统 WebKitGTK,需按发行版与架构逐一验证 | | 鸿蒙 HarmonyOS | 引擎可通过 OHOS NDK 交叉编译 + ArkTS 壳复用同一套界面;Tauri 本身不支持鸿蒙 | | 服务器端 | **零安装**。有 SFTP 即可用;装了 rsync 可启用块级增量加速 | ## 开发 ```bash cargo test --workspace # 引擎行为测试(双向、冲突归档、删除保护、忽略规则) cargo clippy --workspace --all-targets cargo fmt --all ``` 不想碰命令行的话,双击 `脚本\一键构建.bat`:编译、放进 `交付物\`、换掉正在跑的那份并启动,中文提示一步一步告诉你发生了什么。 ### 构建产物 只有一个文件:`交付物\ruying.exe`(约 6.0 MB)。双击=托盘程序,带参数=命令行(`ruying.exe status`、`ruying.exe plan site`),不打安装包、不打 zip、不另出命令行版。 改了界面(`crates/rpc/ui/status.html`)必须重新编译——面板是编译期嵌进 exe 的,改文件不会自动生效。换了品牌图(`assets/icon-source.png`)要跑 `cd apps/desktop && npm run icon` 重生成全套图标,并把 `icons/64x64.png` 拷成 `crates/rpc/ui/brand.png`(面板和浏览器标签页用的是它),三处必须同源。 ### 目录约定 | 目录 | 放什么 | |---|---| | `交付物/` | 只有当前版本的 `ruying.exe`。不进 Git,对外发布走 Gitee 发行版附件(附件名保持 `ruying.exe`,版本号在标签里) | | `输出文件/` | 过程产物:截图、验证报告、临时导出。不进 Git | | `脚本/` | 一键构建、一键发布、更新日志、面板自检脚本 | | `version-stable.txt` / `version-beta.txt` | 两个通道的最新版本号,发布脚本自动写入,软件查新版读的就是它 | 面板自检(改界面后跑):`脚本\面板自检\` 下 `panel_check.js`(jsdom 冒烟测试)与 `panel_shot.js`(出截图核看排版)。 - 工具链:Rust stable + 平台原生构建工具(Windows 需要 MSVC C++ 生成工具,macOS 需要 Xcode CLT,Linux 需要 webkit2gtk 开发包)。 - 国内网络建议把 rustup 与 crates 源指到镜像: `RUSTUP_DIST_SERVER=https://rsproxy.cn`、`RUSTUP_UPDATE_ROOT=https://rsproxy.cn/rustup`, 并在 `~/.cargo/config.toml` 里把 `crates-io` 替换为 `sparse+https://rsproxy.cn/index/`。 - 本仓库的 `.cargo/config.toml` 已把 `target/` 重定向到仓库之外,避免构建产物污染云盘同步目录。 ## 路线图 - [x] v0.1 引擎核心:profile、三方比较、索引、冲突镜像归档、删除保护、忽略规则、CLI - [x] v0.2 SFTP 传输引擎 + 多任务中枢 + 本机控制接口 + 内置状态面板 - [x] v0.2 桌面壳:Tauri 托盘浮窗(点图标弹面板,不跳浏览器)、开机自启、Windows 安装包与绿色版 - [x] v0.3 逐文件进度上报:浮窗变成 OneDrive 式同步监视器;待确认就地处理(含「两边都留」);宽屏两栏 + 独立详情窗 - [x] v0.3.1 内容一致自动收敛;传输记录落盘;右栏记录优先 + 详情页签;界面只在数据变化时重绘 - [x] v0.3.3 保存即单文件同步;直接改名;本地监听 + 低频全量;首次同步手动确认 + 空目录从服务器拉回 - [x] v0.3.4 记录页改成一张文件表(类型图标 + 状态在行里);正在传的文件立刻进表;归档按钮说清取回哪一版 - [x] v0.3.5 交付只留一个 `ruying.exe`(不再出安装包、zip、单独命令行版);固定运行目录 + 开机自启 + 自动更新;品牌标志全软件统一,面板新增「设置」页 - [x] v0.3.6 静默断线自动重连、卡住变红写明原因;远端批量扫描失败只冷却 10 分钟;已经在跑时再双击只弹托盘面板,不再开浏览器 - [x] v0.3.7 托盘角标显示剩余文件数与转圈、菜单顶行说状态;待确认卡片并排展示两侧现状与「会发生什么」,可整边批量定;「版本备份」页可只看需要处理的;滚动条跟随主题 - [x] v0.3.8 自己删掉的文件的留档收进「已删除的文件」,默认收起;这类留档改说「删除前留的副本 / 把这个文件恢复回来」 - [x] v0.3.9 版本备份每条写出「为什么是这一版」(时间差、任务策略、你选过的),按钮改成「哪一版 → 电脑」的箭头写法;页头说明压成一行 - [x] v0.3.10 同步状态只在托盘图标上体现,启动不弹面板;点托盘开面板、再点一下收起 - [x] v0.3.11 旧版留下、没记原因的备份也直接写出任务设的是哪条策略;备份来自电脑时按钮写「电脑旧版 → 电脑」 - [ ] v0.4 归档浏览与批量恢复、冲突两侧内容对比 - [ ] v0.5 rsync 加速引擎(可选)、限速、计划任务 - [ ] v1.0 稳定发布 + 代码签名 + 中文文档站 - [ ] 鸿蒙 ArkTS 壳(复用同一引擎与界面资源) ## 许可证 AGPL-3.0-only。用得起、改得动、闭源商用不行——基于它分发的产品(含通过网络提供服务)必须同样开源。需要商业授权请通过下面的联系方式沟通。 - 作者:书笙 · - 仓库:(Issues、发行版下载都在这里) - 软件主页: