幽冥大陆(九十二 )Gitee 自动化打包JS对接IDE —东方仙盟练气期

在前端开发场景中,我们经常需要直接与代码仓库交互,比如读取配置文件、提交静态资源变更。本文将基于纯浏览器环境 ,实现一个对标 PHP GitHub 类的 Gitee 操作工具类 ,支持列举目录文件、读取文件内容、API 提交代码三大核心功能,无需后端 (Node.js) 中转,直接对接 Gitee Open API v5。

核心约束与前置准备

1. 纯前端限制说明

  • 浏览器同源策略 限制:Gitee API 未完全开放跨域访问,直接调用会触发 CORS 错误。
  • 解决方案:使用 Gitee 官方跨域代理GitHub Pages 反向代理(本文提供代理配置方案)。
  • 令牌安全:前端直接暴露 Gitee 私人令牌存在风险,建议使用临时令牌后端签名校验(演示环境可忽略)。

2. 前置准备步骤

  1. 生成 Gitee 私人令牌 :登录 Gitee → 个人设置 → 私人令牌 → 生成,勾选 repo 权限,保存令牌。

  2. 配置跨域代理 :推荐使用 Cloudflare Workers 搭建免费反向代理,消除 CORS 限制。代理核心代码(Cloudflare Workers):

    javascript

    运行

    复制代码
    addEventListener('fetch', event => {
      event.respondWith(handleRequest(event.request))
    })
    async function handleRequest(request) {
      const url = new URL(request.url);
      const giteeUrl = `https://gitee.com/api/v5${url.pathname}${url.search}`;
      const modifiedRequest = new Request(giteeUrl, {
        method: request.method,
        headers: request.headers,
        body: request.body
      });
      const response = await fetch(modifiedRequest);
      const modifiedResponse = new Response(response.body, response);
      // 允许跨域
      modifiedResponse.headers.set('Access-Control-Allow-Origin', '*');
      modifiedResponse.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
      modifiedResponse.headers.set('Access-Control-Allow-Headers', 'Authorization, Content-Type');
      return modifiedResponse;
    }
  3. 获取代理地址 :部署 Cloudflare Workers 后,得到代理域名(如 https://gitee-proxy.yourname.workers.dev)。

纯前端 Gitee 操作类完整代码

该类完全对标 PHP GitHub 类结构,使用原生 fetch API,无任何第三方依赖,直接在浏览器中运行。

html

预览

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>纯前端 Gitee 操作工具</title>
  <style>
    .container { width: 800px; margin: 50px auto; }
    pre { background: #f5f5f5; padding: 15px; border-radius: 5px; overflow-x: auto; }
    button { padding: 8px 15px; margin: 5px; cursor: pointer; }
  </style>
</head>
<body>
  <div class="container">
    <h2>纯前端 Gitee 操作演示</h2>
    <div>
      <button onclick="testListFiles()">1. 列举根目录文件</button>
      <button onclick="testReadFile()">2. 读取 README.md</button>
      <button onclick="testPushCode()">3. 提交测试文件</button>
    </div>
    <pre id="result"></pre>
  </div>

  <script>
    /**
     * 纯前端 Gitee 操作类(兼容浏览器,无 Node.js 依赖)
     * 支持:列举目录、读取文件、提交代码
     * 依赖:跨域代理(Cloudflare Workers)
     */
    class 未来之窗_FASG_星蕴隐道_开发_gitee {
      /**
       * 构造函数初始化配置
       * @param {string} token Gitee 私人令牌
       * @param {string} proxyUrl 跨域代理地址(必填)
       * @param {string} owner 仓库所有者(默认:cyberwin)
       * @param {string} repo 仓库名称(默认:fauryalliancerustdesk)
       */
      constructor(token, proxyUrl, owner = 'cyberwin', repo = 'fauryalliancerustdesk') {
        this.token = token;
        this.proxyUrl = proxyUrl; // 跨域代理地址
        this.owner = owner;
        this.repo = repo;
        // 代理后的 API 基础地址
        this.apiBaseUrl = `${this.proxyUrl}/repos/${this.owner}/${this.repo}`;
      }

      /**
       * 工具方法:统一发起带跨域的请求
       * @param {string} url 请求地址
       * @param {string} method 请求方法
       * @param {object} data 请求体
       * @returns {object} 格式化响应结果
       */
      async request(url, method = 'GET', data = null) {
        try {
          const headers = {
            'Authorization': `token ${this.token}`,
            'Content-Type': 'application/json; charset=utf-8',
            'User-Agent': 'Browser-Gitee-Handler'
          };

          const options = { method, headers };
          if (['POST', 'PUT', 'DELETE'].includes(method) && data) {
            options.body = JSON.stringify(data);
          }

          const response = await fetch(url, options);
          const httpCode = response.status;
          let responseData = {};
          try {
            responseData = await response.json();
          } catch (e) {
            responseData = await response.text();
          }

          return {
            code: httpCode,
            data: responseData,
            message: httpCode === 200 ? '操作成功' : '请求失败'
          };
        } catch (e) {
          return { code: -1, data: {}, message: `请求异常:${e.message}` };
        }
      }

      /**
       * 功能1:列举仓库目录下的文件/子目录
       * @param {string} path 目录路径(默认根目录)
       * @returns {object} 目录文件列表
       */
      async listRepoFiles(path = '') {
        const apiUrl = `${this.apiBaseUrl}/contents/${path}`;
        const result = await this.request(apiUrl, 'GET');
        if (result.code === 200) {
          return {
            code: 200,
            data: result.data.map(item => ({
              name: item.name,
              path: item.path,
              type: item.type // dir/file
            })),
            message: '目录文件列举成功(纯前端模式)'
          };
        } else {
          return { code: result.code, data: [], message: `列举失败:${JSON.stringify(result.data)}` };
        }
      }

      /**
       * 功能2:读取单个文件内容(预览模式+API模式兜底)
       * @param {string} filePath 文件路径
       * @returns {object} 文件内容及信息
       */
      async readSingleFile(filePath) {
        if (!filePath) return { code: -2, data: {}, msg: '文件路径不能为空' };

        try {
          // 1. 预览模式:直接读取原始文件(免令牌,优先使用)
          const previewUrl = `${this.proxyUrl}/${this.owner}/${this.repo}/raw/main/${filePath}`;
          const previewRes = await fetch(previewUrl);
          if (previewRes.status === 200) {
            const content = await previewRes.text();
            return {
              code: 200,
              data: { file_path: filePath, content, encoding: 'utf-8' },
              msg: '文件读取成功(预览模式)'
            };
          }

          // 2. API 模式兜底:需令牌,Base64 解码
          const apiUrl = `${this.apiBaseUrl}/contents/${filePath}`;
          const apiRes = await this.request(apiUrl, 'GET');
          if (apiRes.code === 200) {
            const content = atob(apiRes.data.content);
            return {
              code: 200,
              data: {
                file_path: filePath,
                content,
                encoding: apiRes.data.encoding,
                sha: apiRes.data.sha
              },
              msg: '文件读取成功(API 模式)'
            };
          } else {
            return { code: apiRes.code, data: {}, msg: `读取失败:${JSON.stringify(apiRes.data)}` };
          }
        } catch (e) {
          return { code: -1, data: {}, msg: `读取异常:${e.message}` };
        }
      }

      /**
       * 功能3:纯前端提交代码(自动处理 SHA,支持创建/更新文件)
       * @param {string} filePath 文件路径
       * @param {string} fileContent 文件内容
       * @param {string} commitMsg 提交备注
       * @param {object} params 可选参数(分支、作者信息)
       * @returns {object} 提交结果
       */
      async pushCodeCommit(filePath, fileContent, commitMsg, params = {}) {
        if (!filePath || !commitMsg) {
          return { code: -2, data: {}, msg: '文件路径和提交备注不能为空' };
        }

        // 默认参数配置
        const defaultParams = {
          branch: 'main',
          author_name: 'Browser-Gitee-Client',
          author_email: 'gitee-client@example.com'
        };
        const finalParams = { ...defaultParams, ...params };

        try {
          // 步骤1:获取文件 SHA(更新文件必需)
          const fileInfo = await this.readSingleFile(filePath);
          const fileSha = fileInfo.code === 200 && fileInfo.data.sha ? fileInfo.data.sha : '';

          // 步骤2:Base64 编码内容(中文兼容)
          const base64Content = btoa(unescape(encodeURIComponent(fileContent)));

          // 步骤3:API 提交文件
          const apiUrl = `${this.apiBaseUrl}/contents/${filePath}`;
          const postData = {
            message: commitMsg,
            content: base64Content,
            sha: fileSha,
            branch: finalParams.branch,
            author: {
              name: finalParams.author_name,
              email: finalParams.author_email
            }
          };

          const pushRes = await this.request(apiUrl, 'PUT', postData);
          if ([200, 201].includes(pushRes.code)) {
            return { code: 200, data: pushRes.data, msg: '代码提交成功(纯前端)' };
          } else {
            return { code: pushRes.code, data: {}, msg: `提交失败:${JSON.stringify(pushRes.data)}` };
          }
        } catch (e) {
          return { code: -1, data: {}, msg: `提交异常:${e.message}` };
        }
      }
    }

    // ==================== 前端演示配置 ====================
    // 替换为你的配置
    const GITEE_TOKEN = '你的Gitee私人令牌';
    const PROXY_URL = '你的Cloudflare Workers代理地址';
    // 初始化 Gitee 操作实例
    const giteeHandler = new 未来之窗_FASG_星蕴隐道_开发_gitee(GITEE_TOKEN, PROXY_URL);

    // 结果输出函数
    function showResult(data) {
      document.getElementById('result').innerText = JSON.stringify(data, null, 2);
    }

    // 测试函数1:列举目录文件
    async function testListFiles() {
      const result = await giteeHandler.listRepoFiles();
      showResult(result);
    }

    // 测试函数2:读取 README.md
    async function testReadFile() {
      const result = await giteeHandler.readSingleFile('README.md');
      showResult(result);
    }

    // 测试函数3:提交测试文件
    async function testPushCode() {
      const result = await giteeHandler.pushCodeCommit(
        'frontend-test.txt',
        '这是纯前端浏览器提交的测试内容!',
        'feat: 新增前端测试文件',
        { author_name: '前端开发者', author_email: 'dev@example.com' }
      );
      showResult(result);
    }
  </script>
</body>
</html>

核心功能解析

1. 跨域处理核心

  • 借助 Cloudflare Workers 搭建反向代理,添加 Access-Control-* 响应头,突破浏览器同源限制。
  • 类的构造函数强制传入 proxyUrl,确保所有请求走代理通道。

2. 三大功能对标 PHP 版本

  • listRepoFiles:调用 Gitee Contents API,格式化返回文件 / 目录的名称、路径、类型,与 PHP 类输出结构一致。
  • readSingleFile:优先使用免令牌的原始文件预览地址,失败后自动切换到 API 模式,自动处理 Base64 解码。
  • pushCodeCommit:自动获取文件 SHA(更新文件必需),对中文内容做兼容的 Base64 编码,支持自定义分支和作者信息。

3. 前端演示交互

  • 页面提供三个测试按钮,分别对应三大核心功能。
  • 结果通过 JSON.stringify 格式化输出,便于调试。

安全与优化建议

  1. 令牌安全 :前端直接存储令牌存在泄露风险,生产环境建议通过后端接口动态获取临时令牌,或使用 OAuth 授权流程。
  2. 代理优化:可在代理中添加请求频率限制,防止令牌滥用。
  3. 错误处理:可扩展类的错误处理逻辑,添加重试机制,提升弱网环境下的稳定性。

阿雪技术观

在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。

Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets, hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. We're gonna team up and explore the whole silicon - based life thing, and in the process, we'll be fueling the growth of technology

相关推荐
戌中横14 小时前
JavaScript——Web APIs DOM
前端·javascript·html
Beginner x_u14 小时前
如何解释JavaScript 中 this 的值?
开发语言·前端·javascript·this 指针
HWL567915 小时前
获取网页首屏加载时间
前端·javascript·vue.js
速易达网络16 小时前
基于RuoYi-Vue 框架美妆系统
前端·javascript·vue.js
运维行者_16 小时前
2026 技术升级,OpManager 新增 AI 网络拓扑与带宽预测功能
运维·网络·数据库·人工智能·安全·web安全·自动化
yinmaisoft17 小时前
JNPF 表单模板实操:高效复用表单设计指南
前端·javascript·html
37方寸17 小时前
前端基础知识(JavaScript)
开发语言·前端·javascript
Whisper_Sy17 小时前
Flutter for OpenHarmony移动数据使用监管助手App实战 - 应用列表实现
android·开发语言·javascript·flutter·php
json{shen:"jing"}18 小时前
1. 两数之和
前端·javascript·数据库
github.com/starRTC18 小时前
Claude Code中英文系列教程19:使用subagent子代理与创建自定义子代理【重要】
前端·javascript·数据库