Typora插件项目中的自动升级功能解析

Typora插件项目中的自动升级功能解析

【免费下载链接】typora_plugin Typora plugin. feature enhancement tool | Typora 插件,功能增强工具 【免费下载链接】typora_plugin 项目地址: https://gitcode.com/gh_mirrors/ty/typora_plugin

痛点:插件升级的困扰

作为Typora用户,你是否曾经遇到过这样的困扰:

  • 发现新版本插件发布,但手动下载安装过程繁琐
  • 担心升级过程中丢失自定义配置和插件
  • 网络环境限制导致无法直接从GitHub下载更新
  • 希望插件能够自动检测并提示新版本

Typora插件项目的自动升级功能正是为了解决这些痛点而生,它提供了智能、安全、便捷的升级体验。

升级功能架构解析

核心类结构

自动升级功能主要由三个核心类构成:

mermaid

升级流程详解

自动升级功能遵循严谨的六步流程:

mermaid

1. 准备工作阶段
prepare = async () => {
    console.log("[1/6] prepare: ensure work dir");
    this.pkgFsExtra.ensureDir(this.workDir);
    await this.chmod();
}

此阶段创建临时工作目录并确保插件目录有足够的权限。

2. 版本检查机制

版本检查是升级流程的核心,通过对比GitHub Release API返回的最新版本信息和本地版本文件来判断是否需要更新:

checkNeedUpdate = async (url = this.latestReleaseUrl) => {
    console.log("[2/6] check if update is needed");
    const _getLatestVersion = async () => {
        const resp = await this.utils.fetch(url, this.requestOption);
        return resp.json()
    }
    const _getCurrentVersion = async () => {
        try {
            const exist = await this.utils.existPath(this.versionFile)
            if (exist) {
                return this.pkgFsExtra.readJson(this.versionFile);
            }
        } catch (e) {
            console.debug("not exist version.json");
        }
    }

    this.latestVersionInfo = await _getLatestVersion();
    this.currentVersionInfo = await _getCurrentVersion();
    if (!this.currentVersionInfo) return true;

    const result = this.utils.compareVersion(
        this.latestVersionInfo.tag_name, 
        this.currentVersionInfo.tag_name
    );
    return result !== 0
}
3. 文件保护策略

升级过程中最重要的保护机制是排除用户自定义文件:

excludeFiles = async () => {
    console.log("[5/6] exclude files");
    const oldDir = this.utils.joinPath(this.customPluginDir);
    const newDir = this.pkgPath.join(this.unzipDir, this.customPluginDir);

    // 对比新旧插件目录,确保自定义插件不被覆盖
    const oldFds = await this.pkgFsExtra.readdir(oldDir);
    const newFds = await this.pkgFsExtra.readdir(newDir);

    const excludeFds = new Set([...newFds])
    oldFds.forEach(name => {
        const exclude = excludeFds.has(name) || 
            (this.pkgPath.extname(name) === ".js") && 
            excludeFds.has(name.substring(0, name.lastIndexOf(".")))
        if (!exclude) {
            const path = this.pkgPath.join(this.customPluginDir, name)
            this.exclude.push(path)
        }
    })

    // 保护用户配置文件和个人文件
    for (const file of this.exclude) {
        const oldPath = this.utils.joinPath(file);
        const newPath = this.pkgPath.join(this.unzipDir, file);
        const exists = await this.utils.existPath(oldPath);
        if (exists) {
            await this.pkgFsExtra.copy(oldPath, newPath);
        }
    }
}

保护的文件包括:

  • ./plugin/global/user_space - 用户空间文件
  • ./plugin/global/user_styles - 用户样式文件
  • ./plugin/global/settings/settings.user.toml - 用户设置
  • ./plugin/global/settings/custom_plugin.user.toml - 自定义插件设置
  • 用户自定义插件目录中的所有非标准插件

配置选项详解

自动升级功能提供了丰富的配置选项:

配置项类型默认值说明
AUTO_UPDATE布尔true是否启用自动更新
START_UPDATE_INTERVAL数字60000启动后首次检查更新的延迟时间(毫秒)
UPDATE_LOOP_INTERVAL数字3600000自动检查更新的间隔时间(毫秒)
NETWORK_REQUEST_TIMEOUT数字30000网络请求超时时间(毫秒)
PROXY字符串""网络中转服务地址

配置示例

[updater]
AUTO_UPDATE = true
START_UPDATE_INTERVAL = 30000    # 30秒后开始检查
UPDATE_LOOP_INTERVAL = 7200000   # 每2小时检查一次
NETWORK_REQUEST_TIMEOUT = 15000  # 15秒超时
PROXY = "http://127.0.0.1:7890"  # 使用本地网络服务

网络服务支持机制

考虑到不同网络环境,升级功能内置了完善的网络服务支持:

自动网络服务检测

class ProxyGetter {
    getProxy = () => {
        if (File.isLinux) {
            return this.getLinuxProxy()
        } else if (File.isWin) {
            return this.getWindowsProxy()
        } else if (File.isMac) {
            return this.getMacProxy()
        } else {
            return ""
        }
    }
    
    getWindowsProxy = () => this._getProxy(
        `reg query "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" | findstr /i "ProxyEnable proxyserver"`,
        stdout => {
            const match = stdout.match(/ProxyEnable.+?0x(?<enable>\d)\r\n.+?ProxyServer\s+REG_SZ\s+(?<proxy>.*)/i)
            return (match && match.groups && match.groups.enable === "1")
                ? match.groups.proxy
                : null
        }
    )
}

手动网络服务设置

用户也可以通过界面手动设置网络服务:

call = async (action, meta) => {
    if (this.config.PROXY) {
        await this.manualUpdate()
        return
    }
    const proxy = await this.getDefaultProxy()
    const op = {
        title: this.pluginName,
        schema: [
            { fields: [{ type: "hint", hintHeader, hintDetail }] },
            { fields: [{ type: "text", key: "proxy", label, placeholder: "http://127.0.0.1:7890" }] },
        ],
        data: { proxy },
    }
    const { response, data } = await this.utils.formDialog.modal(op)
    if (response === 1) {
        await this.manualUpdate(data.proxy)
    }
}

升级模式对比

自动升级功能支持两种升级模式:

静默升级模式

silentUpdate = async proxy => {
    console.log("start silent update...");
    const updater = await this.getUpdater(proxy);
    await updater.run();
}

特点:

  • 后台自动运行
  • 无用户界面干扰
  • 适合定期自动更新

手动升级模式

manualUpdate = async proxy => {
    const timeout = Math.max(this.config.NETWORK_REQUEST_TIMEOUT, 30 * 1000)
    const close = this.utils.notification.show(I18N.pleaseWait)
    const updater = await this.getUpdater(proxy, timeout)
    const { state, info } = await updater.runWithProgressBar(timeout)
    close()
    
    // 显示升级结果
    let msg, msgType, detail
    if (state === "UPDATED") {
        msg = I18N.success
        msgType = "success"
        detail = JSON.stringify(info, null, "\t")
    } else if (state === "NO_NEED") {
        msg = I18N.noNeed
        msgType = "success"
    } else {
        msg = I18N.failed
        msgType = "error"
        detail = state.stack || I18N.unknownError
    }
    this.utils.notification.show(msg, msgType, 10000)
}

特点:

  • 显示进度条
  • 提供详细的升级结果
  • 支持用户交互

错误处理与恢复机制

升级功能具备完善的错误处理:

网络错误处理

try {
    const resp = await this.utils.fetch(url, this.requestOption);
    return resp.buffer()
} catch (error) {
    console.error("Download failed:", error);
    throw new Error(`Download failed: ${error.message}`);
}

文件操作错误处理

syncDir = async () => {
    console.log("[6/6] sync dir");
    const src = this.pkgPath.join(this.unzipDir, this.pluginDir);
    const dst = this.utils.joinPath(this.pluginDir);
    await this.pkgFsExtra.emptyDir(dst);
    await this.pkgFsExtra.copy(src, dst);
    await this.pkgFsExtra.emptyDir(this.workDir);
    if (this.latestVersionInfo) {
        await this.pkgFsExtra.writeJson(this.versionFile, this.latestVersionInfo);
    }
}

状态码说明

状态码含义处理方式
UPDATED升级成功显示成功信息,更新版本文件
NO_NEED无需升级显示当前已是最新版本
Error升级失败显示错误详情,保留原有文件

最佳实践指南

1. 配置建议

# 推荐配置
[updater]
AUTO_UPDATE = true
START_UPDATE_INTERVAL = 60000      # 1分钟后开始检查
UPDATE_LOOP_INTERVAL = 86400000    # 每天检查一次
NETWORK_REQUEST_TIMEOUT = 10000    # 10秒超时

2. 网络环境优化

如果遇到下载速度慢的问题,可以:

  1. 设置网络服务:在插件配置中设置合适的网络服务地址
  2. 调整超时时间:根据网络状况适当增加超时时间
  3. 手动下载:如果自动升级失败,可以从发布页面手动下载

3. 故障排除

常见问题及解决方案:

问题现象可能原因解决方案
升级失败网络连接问题检查网络设置,配置网络服务
文件权限错误目录权限不足以管理员权限运行Typora
版本冲突自定义插件冲突检查自定义插件兼容性

技术亮点总结

  1. 智能版本检测:基于GitHub Release API的版本比较机制
  2. 安全升级:完善的用户文件保护策略,确保自定义配置不丢失
  3. 网络适应性:内置网络服务支持和超时机制,适应各种网络环境
  4. 用户体验优化:提供静默和手动两种升级模式,满足不同需求
  5. 错误恢复:完善的错误处理机制,确保升级失败时系统稳定性

通过这套自动升级系统,Typora插件项目为用户提供了安全、便捷、可靠的升级体验,大大降低了维护成本,让用户能够专注于创作本身。

未来展望

随着项目的不断发展,自动升级功能还将继续优化:

  1. 增量更新:支持差分更新,减少下载流量
  2. 多源支持:除了GitHub,支持更多更新源
  3. 回滚机制:提供版本回滚功能
  4. 更新预览:支持查看更新日志和变更内容

自动升级功能是Typora插件项目持续进化的重要保障,它让插件生态保持活力,为用户带来更好的使用体验。

【免费下载链接】typora_plugin Typora plugin. feature enhancement tool | Typora 插件,功能增强工具 【免费下载链接】typora_plugin 项目地址: https://gitcode.com/gh_mirrors/ty/typora_plugin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值