OpenCode Deployer commited on
Commit
6c277ab
·
1 Parent(s): ad5225a
.claude/CLAUDE.md ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claude Code 兼容配置
2
+ # Hugging Face Spaces 部署版本
3
+
4
+ ## 🌐 语言设置
5
+ **默认语言**: 中文
6
+ **项目语言**: 中文优先
7
+
8
+ ## 🚀 系统信息
9
+ **部署平台**: Hugging Face Spaces
10
+ **服务类型**: OpenCode AI Web Interface
11
+ **技术栈**: Docker + Node.js + Ubuntu 22.04
12
+
13
+ ## 🛠️ 可用工具
14
+ - **定时任务**: Linux cron (已安装)
15
+ - **网络访问**: HTTP/HTTPS 支持
16
+ - **文件操作**: 完整读写权限 (在允许范围内)
17
+ - **代码执行**: Node.js 18.x 环境
18
+
19
+ ## 📋 指导原则
20
+ 1. **中文优先**: 使用中文进行交流、注释和文档
21
+ 2. **容器感知**: 考虑 Docker 容器环境的限制
22
+ 3. **资源友好**: 注意内存和 CPU 使用
23
+ 4. **安全第一**: 避免危险操作和代码
24
+
25
+ ## 🔄 定时任务
26
+ 支持设置定时任务来自动化重复性工作。
27
+
28
+ ---
29
+
30
+ **配置版本**: v1.0
31
+ **兼容性**: Claude Code 格式
32
+ **部署环境**: Hugging Face Spaces
.config/opencode/AGENTS.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 系统级 AGENT.md
2
+
3
+ 此文件包含在系统级别为智能编码代理提供的通用指导原则和最佳实践。
4
+
5
+ ## 交流语言规范
6
+
7
+ ### 默认语言
8
+ - **交流语言**: 默认使用中文进行所有交流和说明
9
+ - **注释语言**: 代码注释优先使用中文,除非项目要求使用英文
10
+ - **文档语言**: 技术文档和说明文档优先使用中文编写
11
+ - **错误信息**: 向用户展示的错误信息使用中文说明
12
+
13
+ ### 语言切换原则
14
+ - 当用户明确要求使用英文时,切换到英文交流
15
+ - 处理国际化项目时,遵循项目的语言规范
16
+ - 在多语言环境中,根据上下文选择合适的语言
17
+
18
+ ## 定时任务
19
+ - 你安装了 linux cron 定时组件,用来设置用户的定时任务
20
+
Dockerfile CHANGED
@@ -1,69 +1,52 @@
1
- FROM ubuntu:22.04 AS base
 
2
 
 
3
  ENV DEBIAN_FRONTEND=noninteractive
4
- ENV PYTHONUNBUFFERED=1
5
 
6
- RUN apt-get update && \
7
- apt-get install -y --no-install-recommends \
8
- software-properties-common \
9
- curl \
10
- ca-certificates \
11
- build-essential && \
12
- add-apt-repository ppa:deadsnakes/ppa -y && \
13
- apt-get update && \
14
- apt-get install -y --no-install-recommends \
15
- python3.12 \
16
- python3.12-dev \
17
- python3.12-venv \
18
- python3.12-distutils \
19
- python3-pip && \
20
- rm -rf /var/lib/apt/lists/*
21
 
22
- FROM base AS python-builder
 
 
 
23
 
24
- RUN python3.12 -m venv /opt/venv
25
- ENV PATH="/opt/venv/bin:$PATH"
26
 
27
- RUN pip install --no-cache-dir --upgrade pip wheel setuptools
 
 
28
 
29
- WORKDIR /app
30
-
31
- COPY requirements.txt .
32
- RUN pip install --no-cache-dir -r requirements.txt
33
-
34
- FROM base AS node-builder
35
-
36
- RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
37
- apt-get install -y --no-install-recommends nodejs && \
38
- rm -rf /var/lib/apt/lists/*
39
-
40
- RUN node --version && npm --version
41
-
42
- FROM python-builder AS final
43
-
44
- COPY --from=node-builder /usr/bin/node /usr/bin/node
45
- COPY --from=node-builder /usr/bin/npm /usr/bin/npm
46
- COPY --from=node-builder /usr/lib/node_modules /usr/lib/node_modules
47
-
48
- # Ensure node is symlinked in a common PATH location
49
- RUN ln -sf /usr/bin/node /usr/local/bin/node
50
-
51
- WORKDIR /app
52
-
53
- # The PATH is already set from python-builder, no need to redefine unless it's overwritten by node-builder in some scenario.
54
- # Assuming python-builder's PATH is sufficient here.
55
 
56
- COPY main.py .
 
 
 
57
 
58
- # Assuming package.json is for metadata or a simple placeholder, keeping its creation.
59
- RUN echo '{"name": "fastoc", "version": "1.0.0", "description": "FastAPI + Node.js + Python"}' > package.json
 
 
 
60
 
61
- # Verification commands for Python and Node.js versions
62
- RUN python3.12 --version && node --version && npm --version
63
 
64
- EXPOSE 7860
 
65
 
66
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
67
- CMD curl -f http://localhost:7860/health || exit 1
 
68
 
69
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
1
+ # 使用 Ubuntu 基础镜像以确保更好的兼容性
2
+ FROM ubuntu:22.04
3
 
4
+ # 设置环境变量避免交互式提示
5
  ENV DEBIAN_FRONTEND=noninteractive
 
6
 
7
+ COPY service/ /.system/service
8
+ COPY script/ /.system/script
9
+ # RUN chmod +x /service/*.sh
10
+ RUN find /.system -type f -name "*.sh" -exec chmod +x {} \;
11
+ RUN find /.system -type f -name "*.js" -exec chmod +x {} \;
 
 
 
 
 
 
 
 
 
 
12
 
13
+ # 创建 OpenCode 全局配置目录
14
+ COPY .config/ /root/.config
15
+ COPY .claude/ /root/.claude
16
+ RUN mkdir -p /.backup
17
 
18
+ # # /root/.config 目录及子目录下所有的 .md 文件权限修改为:644
19
+ # RUN find /root/.config -type f -name "*.md" -exec chmod 644 {} \;
20
 
21
+ # # 添加健康检查
22
+ # HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
23
+ # CMD curl -f http://localhost:7860/global/health || exit 1
24
 
25
+ # 暴露 Hugging Face Spaces 标准端口
26
+ EXPOSE 7860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # # 设置网络环境变量
29
+ # ENV HTTP_PROXY=
30
+ # ENV HTTPS_PROXY=
31
+ # ENV NO_PROXY=localhost,127.0.0.1,0.0.0.0
32
 
33
+ # # 明确禁用服务器认证,确保公开访问
34
+ # # 清除所有可能导致认证的环境变量
35
+ # ENV OPENCODE_SERVER_PASSWORD=""
36
+ # ENV OPENCODE_SERVER_USERNAME=""
37
+ # ENV OPENCODE_AUTH_REQUIRED=false
38
 
39
+ # # 优化网络配置
40
+ # ENV NODE_OPTIONS="--max-http-header-size=16384 --max-old-space-size=2048"
41
 
42
+ # # 网络优化设置
43
+ # ENV NODE_OPTIONS="--max-http-header-size=16384 --max-old-space-size=2048"
44
 
45
+ # 设置调试级别
46
+ ENV NODE_ENV=production
47
+ ENV LOG_LEVEL=info
48
 
49
+ # 使用 opencode serve 启动服务器
50
+ # 这将启动 API 服务器,内置 Web 界面
51
+ # 添加 CORS 支持以允许跨域访问
52
+ CMD ["/.system/service/start-services.sh"]
Dockerfile.clean DELETED
@@ -1,64 +0,0 @@
1
- FROM ubuntu:22.04 AS base
2
-
3
- ENV DEBIAN_FRONTEND=noninteractive
4
- ENV PYTHONUNBUFFERED=1
5
-
6
- RUN apt-get update && apt-get install -y \
7
- software-properties-common \
8
- curl \
9
- ca-certificates \
10
- build-essential \
11
- && add-apt-repository ppa:deadsnakes/ppa -y \
12
- && apt-get update \
13
- && apt-get install -y \
14
- python3.12 \
15
- python3.12-dev \
16
- python3.12-venv \
17
- python3.12-distutils \
18
- python3-pip \
19
- && rm -rf /var/lib/apt/lists/*
20
-
21
- FROM base AS python-builder
22
-
23
- RUN python3.12 -m venv /opt/venv
24
- ENV PATH="/opt/venv/bin:$PATH"
25
-
26
- RUN pip install --no-cache-dir --upgrade pip setuptools wheel
27
-
28
- WORKDIR /app
29
-
30
- COPY requirements.txt .
31
- RUN pip install --no-cache-dir -r requirements.txt
32
-
33
- FROM base AS node-builder
34
-
35
- RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
36
- && apt-get install -y nodejs \
37
- && rm -rf /var/lib/apt/lists/*
38
-
39
- RUN node --version && npm --version
40
-
41
- FROM python-builder AS final
42
-
43
- COPY --from=node-builder /usr/bin/node /usr/bin/node
44
- COPY --from=node-builder /usr/bin/npm /usr/bin/npm
45
- COPY --from=node-builder /usr/lib/node_modules /usr/lib/node_modules
46
-
47
- RUN ln -sf /usr/bin/node /usr/local/bin/node
48
-
49
- WORKDIR /app
50
-
51
- ENV PATH="/opt/venv/bin:$PATH"
52
-
53
- COPY main.py .
54
-
55
- RUN echo '{"name": "fastoc", "version": "1.0.0", "description": "FastAPI + Node.js + Python"}' > package.json
56
-
57
- RUN python3.12 --version && node --version && npm --version
58
-
59
- EXPOSE 7860
60
-
61
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
62
- CMD curl -f http://localhost:7860/health || exit 1
63
-
64
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dockerfile.multistage DELETED
@@ -1,124 +0,0 @@
1
- # ============================================
2
- # FastOC - FastAPI + Node.js + Python 多语言环境 (多阶段构建)
3
- # ============================================
4
-
5
- # 阶段 1: Node.js 构建阶段
6
- # =========================
7
- FROM node:22-alpine AS node-stage
8
-
9
- # 设置 Node.js 环境变量
10
- ENV NODE_ENV=production
11
- ENV NPM_CONFIG_LOGLEVEL=warn
12
- ENV NPM_CONFIG_PROGRESS=false
13
-
14
- # 创建应用目录
15
- WORKDIR /app/nodejs
16
-
17
- # 如果有 package.json,复制并安装依赖
18
- # 这个阶段为将来可能的 Node.js 应用扩展做准备
19
- # COPY package*.json ./
20
- # RUN npm ci --only=production && npm cache clean --force
21
-
22
- # 验证 Node.js 版本
23
- RUN node --version && npm --version
24
-
25
- # 阶段 2: Python 基础环境构建阶段
26
- # ===============================
27
- FROM ubuntu:22.04 AS python-base
28
-
29
- # 设置非交互式安装环境
30
- ENV DEBIAN_FRONTEND=noninteractive
31
- ENV PYTHONUNBUFFERED=1
32
-
33
- # 安装系统依赖和 Python 3.12
34
- RUN apt-get update && apt-get install -y \
35
- software-properties-common \
36
- curl \
37
- ca-certificates \
38
- build-essential \
39
- && add-apt-repository ppa:deadsnakes/ppa -y \
40
- && apt-get update \
41
- && apt-get install -y \
42
- python3.12 \
43
- python3.12-dev \
44
- python3.12-venv \
45
- python3.12-distutils \
46
- python3-pip \
47
- && rm -rf /var/lib/apt/lists/*
48
-
49
- # 创建 Python 虚拟环境
50
- RUN python3.12 -m venv /opt/venv
51
- ENV PATH="/opt/venv/bin:$PATH"
52
-
53
- # 升级 pip 并安装基础工具
54
- RUN pip install --no-cache-dir --upgrade pip setuptools wheel
55
-
56
- # 验证 Python 环境
57
- RUN python3.12 --version && pip --version
58
-
59
- # 阶段 3: Python 依赖安装阶段
60
- # ==========================
61
- FROM python-base AS python-deps
62
-
63
- WORKDIR /app
64
-
65
- # 复制 Python 依赖文件
66
- COPY requirements.txt .
67
-
68
- # 安装 Python 依赖
69
- # 使用 --no-cache-dir 减少镜像大小
70
- RUN pip install --no-cache-dir -r requirements.txt
71
-
72
- # 阶段 4: Node.js 运行时环境安装阶段
73
- # ==================================
74
- FROM python-base AS runtime
75
-
76
- # 安装 Node.js 运行时
77
- # 从 NodeSource 官方源安装 Node.js 22.x LTS
78
- RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
79
- && apt-get install -y nodejs \
80
- && rm -rf /var/lib/apt/lists/*
81
-
82
- # 验证完整运行时环境
83
- RUN node --version && npm --version && python3.12 --version
84
-
85
- # 阶段 5: 最终应用镜像
86
- # ====================
87
- FROM runtime AS final
88
-
89
- # 设置应用环境变量
90
- ENV PYTHONUNBUFFERED=1
91
- ENV NODE_ENV=production
92
-
93
- # 设置工作目录
94
- WORKDIR /app
95
-
96
- # 从 Python 依赖阶段复制已安装的包
97
- COPY --from=python-deps /opt/venv /opt/venv
98
-
99
- # 确保使用 Python 虚拟环境
100
- ENV PATH="/opt/venv/bin:$PATH"
101
-
102
- # 复制应用代码
103
- COPY main.py .
104
- COPY requirements.txt . # 保留用于参考
105
-
106
- # 设置文件权限
107
- RUN chmod +x /opt/venv/bin/* 2>/dev/null || true
108
-
109
- # 验证应用启动前的环境
110
- RUN python3.12 -c "import fastapi; print('FastAPI 导入成功')" \
111
- && python3.12 -c "import uvicorn; print('Uvicorn 导入成功')" \
112
- && node -e "console.log('Node.js 运行时验证成功')"
113
-
114
- # 暴露端口
115
- EXPOSE 7860
116
-
117
- # 健康检查
118
- # 定期检查应用是否正常运行
119
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
120
- CMD curl -f http://localhost:7860/health || exit 1
121
-
122
- # 容器启动命令
123
- # 使用 uvicorn 作为 ASGI 服务器运行 FastAPI 应用
124
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Dockerfile.optimized DELETED
@@ -1,92 +0,0 @@
1
- # ============================================
2
- # FastOC - FastAPI + Node.js + Python 多语言环境 (优化多阶段构建)
3
- # ============================================
4
-
5
- # 阶段 1: 基础环境构建
6
- # =====================
7
- FROM ubuntu:22.04 AS base
8
-
9
- # 设置非交互式环境
10
- ENV DEBIAN_FRONTEND=noninteractive
11
- ENV PYTHONUNBUFFERED=1
12
-
13
- # 安装系统基础工具和 Python 3.12
14
- RUN apt-get update && apt-get install -y \
15
- software-properties-common \
16
- curl \
17
- ca-certificates \
18
- build-essential \
19
- && add-apt-repository ppa:deadsnakes/ppa -y \
20
- && apt-get update \
21
- && apt-get install -y \
22
- python3.12 \
23
- python3.12-dev \
24
- python3.12-venv \
25
- python3.12-distutils \
26
- python3-pip \
27
- && rm -rf /var/lib/apt/lists/*
28
-
29
- # 阶段 2: Python 环境配置
30
- # =========================
31
- FROM base AS python-builder
32
-
33
- # 创建并配置 Python 虚拟环境
34
- RUN python3.12 -m venv /opt/venv
35
- ENV PATH="/opt/venv/bin:$PATH"
36
-
37
- # 升级 pip 和安装构建工具
38
- RUN pip install --no-cache-dir --upgrade pip setuptools wheel
39
-
40
- WORKDIR /app
41
-
42
- # 安装 Python 依赖
43
- COPY requirements.txt .
44
- RUN pip install --no-cache-dir -r requirements.txt
45
-
46
- # 阶段 3: Node.js 环境构建
47
- # ========================
48
- FROM base AS node-builder
49
-
50
- # 安装 Node.js 22.x LTS
51
- RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
52
- && apt-get install -y nodejs \
53
- && rm -rf /var/lib/apt/lists/*
54
-
55
- # 验证 Node.js 安装
56
- RUN node --version && npm --version
57
-
58
- # 阶段 4: 最终运行时镜像
59
- # =====================
60
- FROM python-builder AS final
61
-
62
- # 从 Node.js 构建阶段复制 Node.js 运行时
63
- COPY --from=node-builder /usr/bin/node /usr/bin/node
64
- COPY --from=node-builder /usr/bin/npm /usr/bin/npm
65
- COPY --from=node-builder /usr/lib/node_modules /usr/lib/node_modules
66
-
67
- # 设置工作目录
68
- WORKDIR /app
69
-
70
- # 确保虚拟环境激活
71
- ENV PATH="/opt/venv/bin:$PATH"
72
-
73
- # 复制应用代码
74
- COPY main.py .
75
-
76
- # 创建一个简单的 package.json 用于可能的 Node.js 扩展
77
- RUN echo '{"name": "fastoc", "version": "1.0.0", "description": "FastAPI + Node.js + Python"}' > package.json
78
-
79
- # 验证完整环境
80
- RUN python3.12 --version && node --version && npm --version \
81
- && python3.12 -c "import fastapi; print('✅ FastAPI 导入成功')" \
82
- && python3.12 -c "import uvicorn; print('✅ Uvicorn 导入成功')"
83
-
84
- # 暴露端口
85
- EXPOSE 7860
86
-
87
- # 健康检查(可选,适合生产环境)
88
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
89
- CMD curl -f http://localhost:7860/health || exit 1
90
-
91
- # 启动命令
92
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main.py DELETED
@@ -1,20 +0,0 @@
1
- from fastapi import FastAPI
2
-
3
- app = FastAPI(title="FastOC - FastAPI + Node.js + Python", version="1.0.0")
4
-
5
- @app.get("/")
6
- async def root():
7
- return {"message": "Hello World from FastOC!", "tech_stack": "FastAPI + Node.js 22.x + Python 3.12"}
8
-
9
- @app.get("/health")
10
- async def health_check():
11
- return {"status": "healthy", "service": "FastOC", "version": "1.0.0"}
12
-
13
- @app.get("/info")
14
- async def get_info():
15
- return {
16
- "service": "FastOC",
17
- "description": "多语言开发环境",
18
- "features": ["FastAPI", "Node.js 22.x", "Python 3.12"],
19
- "endpoints": ["/", "/health", "/info"]
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
push.sh ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # OS_TYPE=$(uname -s)
4
+
5
+ # case "$OS_TYPE" in
6
+ # Darwin*)
7
+ # echo "当前系统是 macOS。"
8
+ # ssh-add ~/.ssh/id_ed25519_airsltd_mac
9
+ # ;;
10
+ # Linux*)
11
+ # echo "当前系统是 Linux。"
12
+ # ;;
13
+ # CYGWIN*|MINGW*|MSYS*)
14
+ # echo "当前系统是 Windows。"
15
+ # ;;
16
+ # *)
17
+ # echo "未知的系统: $OS_TYPE"
18
+ # ;;
19
+ # esac
20
+ git add .
21
+ git commit -m "update"
22
+ git push
23
+
requirements.txt DELETED
@@ -1,2 +0,0 @@
1
- fastapi
2
- uvicorn[standard]
 
 
 
script/backup.sh ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # 备份 /.system 文件
4
+ tar -czvf /.backup/.system.tar.gz --exclude-from=/.system/script/exclude_list_system.txt -C /.system .
5
+
6
+ # 备份 /root 文件
7
+ tar -czvf /.backup/root.tar.gz --exclude-from=/.system/script/exclude_list_root.txt -C /root .
script/exclude_list_root.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # exclude_list_root.txt
2
+ .bun
3
+ .cache
4
+ # .claude
5
+ .config/opencode/node_modules
6
+ .local
7
+ .npm
script/exclude_list_system.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # exclude_list_system.txt
2
+ # 暂时没有需要排除的文件或目录
script/restore.sh ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # 恢复脚本 - 将backup.sh备份的内容恢复到对应目录
4
+
5
+ set -e # 遇到错误时立即退出
6
+
7
+ echo "开始恢复备份内容..."
8
+
9
+ # 检查备份文件是否存在
10
+ if [ ! -f "/.backup/.system.tar.gz" ]; then
11
+ echo "错误:/.backup/.system.tar.gz 备份文件不存在"
12
+ exit 1
13
+ fi
14
+
15
+ if [ ! -f "/.backup/root.tar.gz" ]; then
16
+ echo "错误:/.backup/root.tar.gz 备份文件不存在"
17
+ exit 1
18
+ fi
19
+
20
+ # 创建目标目录(如果不存在)
21
+ mkdir -p /.system
22
+ mkdir -p /root
23
+
24
+ echo "正在恢复 /.system 文件..."
25
+ # 恢复 .system 文件,自动覆盖现有文件
26
+ tar -xzf /.backup/.system.tar.gz -C /.system --overwrite
27
+
28
+ echo "正在恢复 /root 文件..."
29
+ # 恢复 root 文件,自动覆盖现有文件
30
+ tar -xzf /.backup/root.tar.gz -C /root --overwrite
31
+
32
+ echo "恢复完成!"
33
+ echo "已将备份内容恢复到 /.system 和 /root 目录"
script/upload-example.js ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Google Drive 文件上传示例脚本
5
+ * 演示如何使用 /.system/service/gdrive 中的脚本上传文件
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ // 设置环境变量指向 gdrive 服务目录
12
+ process.env.GDRIVE_SERVICE_PATH = '/.system/service/gdrive';
13
+
14
+ // 导入 gdrive-hf 模块 (支持 HuggingFace Space 环境变量)
15
+ const gdriveModule = require(path.join(process.env.GDRIVE_SERVICE_PATH, 'gdrive-hf.js'));
16
+
17
+ /**
18
+ * 创建示例文件
19
+ */
20
+ function createSampleFile(filePath) {
21
+ const content = `这是一个示例文件,用于测试 Google Drive 上传功能。
22
+
23
+ 创建时间: ${new Date().toLocaleString('zh-CN')}
24
+ 文件路径: ${filePath}
25
+ 测试内容: Google Drive API 上传示例
26
+
27
+ 此文件由 /.system/script/upload-example.js 自动生成。
28
+ `;
29
+
30
+ fs.writeFileSync(filePath, content, 'utf8');
31
+ console.log(`示例文件已创建: ${filePath}`);
32
+ }
33
+
34
+ /**
35
+ * 上传示例文件到 Google Drive
36
+ */
37
+ async function uploadSampleFile() {
38
+ try {
39
+ // 示例文件路径
40
+ const sampleFileName = `example_${Date.now()}.txt`;
41
+ const sampleFilePath = path.join(process.cwd(), sampleFileName);
42
+
43
+ // 创建示例文件
44
+ createSampleFile(sampleFilePath);
45
+
46
+ console.log('开始上传到 Google Drive...');
47
+
48
+ // 调用 gdrive 模块的 uploadFile 函数
49
+ const result = await gdriveModule.uploadFile(sampleFilePath);
50
+
51
+ if (result) {
52
+ console.log('\n=== 上传成功 ===');
53
+ console.log('文件 ID:', result.id);
54
+ console.log('文件名:', result.name);
55
+ console.log('文件大小:', result.size, '字节');
56
+ console.log('查看链接:', result.webViewLink);
57
+
58
+ // 清理本地示例文件
59
+ fs.unlinkSync(sampleFilePath);
60
+ console.log(`\n本地示例文件已删除: ${sampleFilePath}`);
61
+ }
62
+
63
+ } catch (error) {
64
+ console.error('上传失败:', error.message);
65
+
66
+ // 提供错误解决建议
67
+ if (error.message.includes('环境变量') || error.message.includes('GOOGLE_')) {
68
+ console.log('\n建议解决方案:');
69
+ console.log('1. 在 HuggingFace Space 的 Settings > Environment Variables 中设置:');
70
+ console.log(' - GOOGLE_CLIENT_ID: Google OAuth 客户端 ID');
71
+ console.log(' - GOOGLE_CLIENT_SECRET: Google OAuth 客户端密钥');
72
+ console.log(' - GOOGLE_REFRESH_TOKEN: Google OAuth 刷新令牌');
73
+ console.log('2. 运行检查配置: cd /.system/service/gdrive && node gdrive-hf.js check');
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * 上传指定文件到 Google Drive
80
+ * @param {string} filePath - 要上传的文件路径
81
+ * @param {string} folderId - 目标文件夹 ID (可选)
82
+ */
83
+ async function uploadSpecificFile(filePath, folderId = null) {
84
+ try {
85
+ if (!fs.existsSync(filePath)) {
86
+ console.error(`文件不存在: ${filePath}`);
87
+ return;
88
+ }
89
+
90
+ console.log(`上传文件: ${filePath}`);
91
+
92
+ // 调用 gdrive 模块的 uploadFile 函数
93
+ const result = await gdriveModule.uploadFile(filePath, folderId);
94
+
95
+ if (result) {
96
+ console.log('\n=== 上传成功 ===');
97
+ console.log('文件 ID:', result.id);
98
+ console.log('文件名:', result.name);
99
+ console.log('文件大小:', result.size, '字节');
100
+ console.log('查看链接:', result.webViewLink);
101
+ }
102
+
103
+ } catch (error) {
104
+ console.error('上传失败:', error.message);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * 显示使用说明
110
+ */
111
+ function showUsage() {
112
+ console.log('使用方法:');
113
+ console.log(' node upload-example.js - 上传示例文件');
114
+ console.log(' node upload-example.js <文件路径> - 上传指定文件');
115
+ console.log(' node upload-example.js <文件路径> <文件夹ID> - 上传到指定文件夹');
116
+ console.log('');
117
+ console.log('示例:');
118
+ console.log(' node upload-example.js');
119
+ console.log(' node upload-example.js ./test.txt');
120
+ console.log(' node upload-example.js ./document.pdf 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms');
121
+ console.log('');
122
+ console.log('注意:');
123
+ console.log('1. 需要在 HuggingFace Space 环境变量中设置 Google Drive 凭据');
124
+ console.log('2. 检查配置: cd /.system/service/gdrive && node gdrive-hf.js check');
125
+ console.log('3. 环境变量: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN');
126
+ }
127
+
128
+ // 主函数
129
+ async function main() {
130
+ const args = process.argv.slice(2);
131
+
132
+ if (args.length === 0) {
133
+ // 没有参数,上传示例文件
134
+ await uploadSampleFile();
135
+ } else if (args.length === 1) {
136
+ // 上传指定文件
137
+ await uploadSpecificFile(args[0]);
138
+ } else if (args.length === 2) {
139
+ // 上��到指定文件夹
140
+ await uploadSpecificFile(args[0], args[1]);
141
+ } else {
142
+ console.error('参数错误');
143
+ showUsage();
144
+ }
145
+ }
146
+
147
+ // 如果直接运行此脚本
148
+ if (require.main === module) {
149
+ main().catch(error => {
150
+ console.error('程序执行失败:', error.message);
151
+ process.exit(1);
152
+ });
153
+ }
154
+
155
+ module.exports = {
156
+ uploadSampleFile,
157
+ uploadSpecificFile,
158
+ createSampleFile
159
+ };
service/cron-service.sh ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ echo "安装cron 服务..."
4
+ apt-get update && apt-get install -y cron
5
+ echo "⏰ 启动 cron 服务..."
6
+ cron -f & # 在后台启动 cron 守护进程,并将其移入后台,以便脚本可以继续执行
7
+ echo "✅ cron 服务状态:$(pgrep -l cron || echo 'cron 服务未运行')"
service/gdrive/README.md ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Google Drive OAuth2.0 桌面客户端
2
+
3
+ 这是一个使用 Google Drive API 进行文件操作的 Node.js 工具,支持 OAuth2.0 桌面客户端认证,提供完整的文件管理功能。
4
+
5
+ ## 🚀 快速开始
6
+
7
+ ### 1. 安装依赖
8
+
9
+ ```bash
10
+ cd /.system/service/gdrive
11
+ npm install
12
+
13
+ 2. 获取 Google Cloud 凭据
14
+
15
+
16
+ 访问 Google Cloud Console
17
+
18
+ 创建新项目或选择现有项目
19
+
20
+ 启用 Google Drive API
21
+
22
+ 创建凭据:
23
+
24
+ 选择"OAuth 客户端 ID"
25
+
26
+ 选择"桌面应用程序"
27
+
28
+ 下载 JSON 文件
29
+
30
+
31
+
32
+ 将 JSON 文件重命名为 credentials.json 并放在当前目录下
33
+
34
+
35
+ 3. 进行认证
36
+
37
+ node gdrive-cli.js auth
38
+
39
+ 按照提示在浏览器中完成认证,认证成功后会生成 token.json 文件。
40
+
41
+
42
+ 📋 功能特性
43
+
44
+
45
+ ✅ OAuth2.0 认证 - 安全的桌面客户端认证
46
+
47
+ ✅ 文件列举 - 列出文件和文件夹
48
+
49
+ ✅ 文件上传 - 上传本地文件到 Google Drive
50
+
51
+ ✅ 文件下载 - 下载 Google Drive 文件到本地
52
+
53
+ ✅ 文件删除 - 删除文件和文件夹
54
+
55
+ ✅ 文件夹管理 - 创建文件夹
56
+
57
+ ✅ 文件搜索 - 按条件搜索文件
58
+
59
+ ✅ 文件信息 - 获取详细文件信息
60
+
61
+ ✅ 令牌管理 - 自动刷新访问令牌
62
+
63
+
64
+ 🛠️ 使用方法
65
+
66
+ 认证命令
67
+
68
+ # 进行OAuth2认证
69
+ node gdrive-cli.js auth
70
+
71
+ # 刷新访问令牌
72
+ node gdrive-cli.js refresh
73
+
74
+ # 测试连接
75
+ node gdrive-cli.js test
76
+
77
+ 文件操作
78
+
79
+ # 列出根目录文件
80
+ node gdrive-cli.js list
81
+
82
+ # 列出指定文件夹的文件
83
+ node gdrive-cli.js list <文件夹ID> <每页数量>
84
+
85
+ # 上传文件到根目录
86
+ node gdrive-cli.js upload /path/to/file.txt
87
+
88
+ # 上传文件到指定文件夹
89
+ node gdrive-cli.js upload /path/to/file.txt <文件夹ID>
90
+
91
+ # 下载文件到当前目录
92
+ node gdrive-cli.js download <文件ID>
93
+
94
+ # 下载文件到指定路径
95
+ node gdrive-cli.js download <文件ID> /path/to/save/location.txt
96
+
97
+ # 删除文件
98
+ node gdrive-cli.js delete <文件ID>
99
+
100
+ # 创建文件夹
101
+ node gdrive-cli.js mkdir "新文件夹"
102
+
103
+ # 在指定文件夹中创建子文件夹
104
+ node gdrive-cli.js mkdir "子文件夹" <父文件夹ID>
105
+
106
+ # 搜索文件
107
+ node gdrive-cli.js search "name contains '.txt'"
108
+
109
+ # 获取文件信息
110
+ node gdrive-cli.js info <文件ID>
111
+
112
+ 📝 使用示例
113
+
114
+ 基本工作流
115
+
116
+ # 1. 认证(首次使用)
117
+ node gdrive-cli.js auth
118
+
119
+ # 2. 测试连接
120
+ node gdrive-cli.js test
121
+
122
+ # 3. 创建文件夹
123
+ node gdrive-cli.js mkdir "我的文档"
124
+
125
+ # 4. 上传文件
126
+ node gdrive-cli.js upload ./document.txt
127
+
128
+ # 5. 列出文件查看上传结果
129
+ node gdrive-cli.js list
130
+
131
+ # 6. 搜索特定文件
132
+ node gdrive-cli.js search "name contains 'document'"
133
+
134
+ # 7. 下载文件测试
135
+ node gdrive-cli.js download <文件ID> ./downloaded_document.txt
136
+
137
+ 高级搜索示例
138
+
139
+ # 搜索所有PDF文件
140
+ node gdrive-cli.js search "mimeType = 'application/pdf'"
141
+
142
+ # 搜索最近修改的文件
143
+ node gdrive-cli.js search "modifiedTime > '2024-01-01T00:00:00'"
144
+
145
+ # 搜索特定名称的文件
146
+ node gdrive-cli.js search "name = '重要文档'"
147
+
148
+ 📁 项目结构
149
+
150
+ /.system/service/gdrive/
151
+ ├── gdrive-service.js # 核心服务类(OAuth2.0 + 文件操作)
152
+ ├── gdrive-cli.js # 命令行接口
153
+ ├── credentials.json # Google Cloud 凭据文件
154
+ ├── token.json # 访问令牌文件(认证后生成)
155
+ ├── package.json # 项目配置
156
+ ├── package-lock.json # 依赖锁定文件
157
+ ├── node_modules/ # 依赖包
158
+ └── README.md # 说明文档
159
+
160
+ 🔧 技术实现
161
+
162
+ OAuth2.0 认证流程
163
+
164
+
165
+ 使用 Google Cloud 凭据创建 OAuth2 客户端
166
+
167
+ 生成认证 URL,用户在浏览器中授权
168
+
169
+ 获取授权码,交换访问令牌
170
+
171
+ 保存令牌到本地文件,支持自动刷新
172
+
173
+
174
+ 文件操作 API
175
+
176
+
177
+ 列举文件: drive.files.list() - 支持分页和过滤
178
+
179
+ 上传文件: drive.files.create() - 支持大文件上传
180
+
181
+ 下载文件: drive.files.get() - 流式下载
182
+
183
+ 删除文件: drive.files.delete() - 软删除到回收站
184
+
185
+ 创建文件夹: drive.files.create() - 指定 MIME 类型
186
+
187
+ 搜索文件: drive.files.list() - 使用查询语法
188
+
189
+
190
+ ⚠️ 注意事项
191
+
192
+
193
+ 安全性:
194
+
195
+ credentials.json 和 token.json 包含敏感信息,请勿分享
196
+
197
+ 建议将这些文件添加到 .gitignore
198
+
199
+
200
+
201
+ 令牌管理:
202
+
203
+ 访问令牌会过期,系统会自动刷新
204
+
205
+ 如遇到认证错误,可运行 node gdrive-cli.js refresh
206
+
207
+
208
+
209
+ API 限制:
210
+
211
+ Google Drive API 有使用配额限制
212
+
213
+ 大量操作时请注意控制频率
214
+
215
+
216
+
217
+ 文件大小:
218
+
219
+ 单文件上传限制为 5GB
220
+
221
+ 超大文件需要使用分块上传
222
+
223
+
224
+
225
+
226
+ 🐛 错误处理
227
+
228
+ 常见错误及解决方法:
229
+
230
+
231
+ 错误信息 解决方法
232
+ "凭据文件不存在" 确保 credentials.json 文件存在
233
+ "认证失败" 删除 token.json 并重新运行 node gdrive-cli.js auth
234
+ "文件不存在" 检查文件路径或文件ID是否正确
235
+ "权限不足" 检查 Google Cloud Console 中的 API 权限设置
236
+ "令牌过期" 运行 node gdrive-cli.js refresh 刷新令牌
237
+
238
+ 🔄 API 查询语法
239
+
240
+ ���索功能支持 Google Drive API 查询语法:
241
+
242
+
243
+ # 按名称搜索
244
+ name contains '关键词'
245
+
246
+ # 按类型搜索
247
+ mimeType = 'application/pdf'
248
+ mimeType contains 'image/'
249
+
250
+ # 按时间搜索
251
+ modifiedTime > '2024-01-01T00:00:00'
252
+ createdTime < '2024-12-31T23:59:59'
253
+
254
+ # 组合条件
255
+ name contains '报告' and modifiedTime > '2024-01-01T00:00:00'
256
+
257
+ 📚 开发参考
258
+
259
+ 作为模块使用
260
+
261
+ const GoogleDriveOAuth2Service = require('./gdrive-service');
262
+
263
+ async function example() {
264
+ const service = new GoogleDriveOAuth2Service();
265
+ await service.initialize();
266
+
267
+ // 列出文件
268
+ const files = await service.listFiles({ pageSize: 20 });
269
+ console.log(files.files);
270
+
271
+ // 上传文件
272
+ const result = await service.uploadFile('./test.txt');
273
+ console.log('上传成功:', result);
274
+ }
275
+
276
+ 支持的方法
277
+
278
+
279
+ initialize() - 初始化认证
280
+
281
+ listFiles(options) - 列出文件
282
+
283
+ uploadFile(filePath, fileName, folderId) - 上传文件
284
+
285
+ downloadFile(fileId, destinationPath) - 下载文件
286
+
287
+ createFolder(folderName, parentFolderId) - 创建文件夹
288
+
289
+ deleteFile(fileId) - 删除文件
290
+
291
+ searchFiles(searchQuery) - 搜索文件
292
+
293
+ getFileInfo(fileId) - 获取文件信息
294
+
295
+ testConnection() - 测试连接
296
+
297
+
298
+ 📄 许可证
299
+
300
+ MIT License
301
+
302
+
303
+ 🤝 贡献
304
+
305
+ 欢迎提交 Issue 和 Pull Request!
service/gdrive/config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "defaultFolder": {
3
+ "name": "myoc",
4
+ "id": "1UCRmKH3yfMq4u6NjMMW9av30oyNjTgFk"
5
+ }
6
+ }
service/gdrive/credentials.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"installed":{"client_id":"769815209820-s0vsl27t45etplsjigcctb3bo8j05599.apps.googleusercontent.com","project_id":"molten-nirvana-445614-b4","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}
service/gdrive/example.js ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+
5
+ Google Drive 使用示例脚本
6
+ 演示如何使用 Google Drive OAuth2 服务 */
7
+ const GoogleDriveOAuth2Service = require('./gdrive-service'); const path = require('path'); const fs = require('fs');
8
+
9
+ async function runExample() { console.log('🚀 Google Drive OAuth2.0 示例演示\n');
10
+
11
+ const service = new GoogleDriveOAuth2Service();
12
+
13
+ try {
14
+ // 1. 初始化服务
15
+ console.log('📡 正在初始化 Google Drive 服务...');
16
+ await service.initialize();
17
+ console.log('✅ 服务初始化成功!\n');
18
+
19
+ // 2. 测试连接
20
+ console.log('🔍 正在测试连接...');
21
+ const connectionInfo = await service.testConnection();
22
+ console.log('✅ 连接成功!');
23
+ console.log(`👤 用户: ${connectionInfo.user.emailAddress}\n`);
24
+
25
+ // 3. 列出文件
26
+ console.log('📋 正在列出根目录文件...');
27
+ const listResult = await service.listFiles({ pageSize: 5 });
28
+
29
+ if (listResult.files && listResult.files.length > 0) {
30
+ console.log(`找到 ${listResult.files.length} 个文件:`);
31
+ listResult.files.forEach((file, index) => {
32
+ const size = file.size ? `${(parseInt(file.size) / 1024).toFixed(2)} KB` : 'N/A';
33
+ const type = file.mimeType.includes('folder') ? '📁' : '📄';
34
+ console.log(` ${index + 1}. ${type} ${file.name} (${size})`);
35
+ });
36
+ } else {
37
+ console.log('📭 根目录为空');
38
+ }
39
+ console.log('');
40
+
41
+ // 4. 创建测试文件夹
42
+ console.log('📁 正在创建测试文件夹...');
43
+ const folderName = `测试文件夹_${new Date().getTime()}`;
44
+ const folderResult = await service.createFolder(folderName);
45
+ console.log(`✅ 文件夹创建成功: ${folderResult.name} (ID: ${folderResult.id})\n`);
46
+
47
+ // 5. 创建测试文件并上传
48
+ console.log('📤 正在创建测试文件并上传...');
49
+ const testContent = `这是一个测试文件\n创建时间: ${new Date().toISOString()}\n此文件由 Google Drive OAuth2.0 示例脚本创建`;
50
+ const testFileName = `test_file_${Date.now()}.txt`;
51
+ const testFilePath = path.join(__dirname, testFileName);
52
+
53
+ // 创建本地测试文件
54
+ fs.writeFileSync(testFilePath, testContent, 'utf8');
55
+ console.log(`📝 本地测试文件已创建: ${testFileName}`);
56
+
57
+ // 上传到测试文件夹
58
+ const uploadResult = await service.uploadFile(testFilePath, testFileName, folderResult.id);
59
+ console.log(`✅ 文件上传成功: ${uploadResult.name} (ID: ${uploadResult.id})\n`);
60
+
61
+ // 6. 搜索文件
62
+ console.log('🔍 正在搜索测试文件...');
63
+ const searchResult = await service.searchFiles(`name = '${testFileName}'`);
64
+ if (searchResult.files && searchResult.files.length > 0) {
65
+ console.log(`✅ 找到 ${searchResult.files.length} 个匹配的文件:`);
66
+ searchResult.files.forEach(file => {
67
+ console.log(` 📄 ${file.name} (ID: ${file.id})`);
68
+ });
69
+ }
70
+ console.log('');
71
+
72
+ // 7. 获取文件信息
73
+ if (uploadResult.id) {
74
+ console.log('ℹ️ 正在获取文件详细信息...');
75
+ const fileInfo = await service.getFileInfo(uploadResult.id);
76
+ console.log(`📄 文件名: ${fileInfo.name}`);
77
+ console.log(`📏 大小: ${fileInfo.size} 字节`);
78
+ console.log(`📋 类型: ${fileInfo.mimeType}`);
79
+ console.log(`📅 创建时间: ${fileInfo.createdTime}`);
80
+ console.log(`🔄 修改时间: ${fileInfo.modifiedTime}\n`);
81
+ }
82
+
83
+ // 8. 下载文件
84
+ if (uploadResult.id) {
85
+ console.log('📥 正在下载文件...');
86
+ const downloadPath = path.join(__dirname, `downloaded_${testFileName}`);
87
+ const downloadResult = await service.downloadFile(uploadResult.id, downloadPath);
88
+ console.log(`✅ 文件下载成功: ${downloadResult.destinationPath}`);
89
+ console.log(`📄 原文件名: ${downloadResult.fileName}`);
90
+ console.log(`📏 文件大小: ${downloadResult.fileSize} 字节\n`);
91
+ }
92
+
93
+ // 9. 清理测试文件和文件夹
94
+ console.log('🧹 正在清理测试文件...');
95
+
96
+ // 删除上传的文件
97
+ if (uploadResult.id) {
98
+ await service.deleteFile(uploadResult.id);
99
+ console.log(`🗑️ 已删除上传的文件: ${uploadResult.name}`);
100
+ }
101
+
102
+ // 删除下载的文件
103
+ if (fs.existsSync(path.join(__dirname, `downloaded_${testFileName}`))) {
104
+ fs.unlinkSync(path.join(__dirname, `downloaded_${testFileName}`));
105
+ console.log(`🗑️ 已删除下载的文件: downloaded_${testFileName}`);
106
+ }
107
+
108
+ // 删除本地测试文件
109
+ if (fs.existsSync(testFilePath)) {
110
+ fs.unlinkSync(testFilePath);
111
+ console.log(`🗑️ 已删除本地测试文件: ${testFileName}`);
112
+ }
113
+
114
+ // 删除测试文件夹
115
+ if (folderResult.id) {
116
+ await service.deleteFile(folderResult.id);
117
+ console.log(`🗑️ 已删除测试文件夹: ${folderResult.name}`);
118
+ }
119
+
120
+ console.log('\n✅ 所有测试��成! 示例运行成功。');
121
+ console.log('\n💡 提示: 你现在可以使用 "node gdrive-cli.js help" 查看所有可用命令。');
122
+
123
+ } catch (error) {
124
+ console.error('❌ 示例运行失败:', error.message);
125
+
126
+ // 提供解决建议
127
+ if (error.message.includes('credentials') || error.message.includes('认证')) {
128
+ console.log('\n💡 解决方案: 请先运行以下命令进行认证:');
129
+ console.log(' node gdrive-cli.js auth');
130
+ } else if (error.message.includes('ENOTFOUND') || error.message.includes('network')) {
131
+ console.log('\n💡 解决方案: 请检查网络连接');
132
+ } else {
133
+ console.log('\n💡 请检查凭据配置和网络连接');
134
+ }
135
+
136
+ process.exit(1);
137
+ }
138
+ }
139
+
140
+ // 如果直接运行此脚本 if (require.main === module) { runExample(); }
141
+
142
+ module.exports = runExample;
service/gdrive/folder-client.js ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+
5
+ Google Drive 文件夹专用客户端
6
+ 限定操作到指定文件夹 */
7
+ const GoogleDriveOAuth2Service = require('./gdrive-service'); const path = require('path'); const fs = require('path');
8
+
9
+ /**
10
+
11
+ 显示使用帮助 */ function showUsage() { console.log(''); console.log('📁 Google Drive 文件夹专用客户端'); console.log('当前配置文件夹: myoc'); console.log(''); console.log('📄 文件操作:'); console.log(' node folder-client.js list [数量] - 列出文件夹内文件'); console.log(' node folder-client.js upload <文件路径> - 上传文件到文件夹'); console.log(' node folder-client.js download <文件ID> - 下载文件'); console.log(' node folder-client.js delete <文件ID> - 删除文件'); console.log(' node folder-client.js mkdir <文件夹名> - 在当前文件夹下创建子文件夹'); console.log(' node folder-client.js search <搜索条件> - 在当前文件夹下搜索'); console.log(' node folder-client.js info <文件ID> - 获取文件信息'); console.log(' node folder-client.js test - 测试连接'); console.log(''); }
12
+ /**
13
+
14
+ 格式化文件信息显示 */ function formatFileInfo(file) { const size = file.size ? ${(parseInt(file.size) / 1024).toFixed(2)} KB : 'N/A'; const type = file.mimeType.includes('folder') ? '📁' : file.mimeType.includes('google-apps') ? '📄' : '📎'; return ${type} ${file.name}\n ID: ${file.id}\n 大小: ${size}\n 修改时间: ${file.modifiedTime}\n;
15
+ }
16
+
17
+ async function main() { const args = process.argv.slice(2);
18
+
19
+ if (args.length === 0) {
20
+ showUsage();
21
+ return;
22
+ }
23
+
24
+ const command = args[0];
25
+
26
+ // myoc 文件夹 ID
27
+ const MYOC_FOLDER_ID = '1UCRmKH3yfMq4u6NjMMW9av30oyNjTgFk';
28
+ const service = new GoogleDriveOAuth2Service(MYOC_FOLDER_ID);
29
+
30
+ try {
31
+ switch (command) {
32
+ case 'list':
33
+ await service.initialize();
34
+ const pageSize = args[1] ? parseInt(args[1]) : 10;
35
+ const result = await service.listFiles({ pageSize });
36
+
37
+ console.log(`📋 myoc 文件夹内容 (${result.files.length} 个):`);
38
+ console.log('');
39
+ result.files.forEach((file, index) => {
40
+ console.log(`${index + 1}. ${formatFileInfo(file)}`);
41
+ });
42
+ break;
43
+
44
+ case 'upload':
45
+ if (!args[1]) {
46
+ console.error('❌ 请指定要上传的文件路径');
47
+ break;
48
+ }
49
+ await service.initialize();
50
+ const uploadResult = await service.uploadFile(args[1]);
51
+ console.log('✅ 文件上传成功!');
52
+ console.log(`📎 文件名: ${uploadResult.name}`);
53
+ console.log(`🆔 文件ID: ${uploadResult.id}`);
54
+ console.log(`🔗 链接: ${uploadResult.webViewLink}`);
55
+ break;
56
+
57
+ case 'download':
58
+ if (!args[1]) {
59
+ console.error('❌ 请指定要下载的文件ID');
60
+ break;
61
+ }
62
+ await service.initialize();
63
+ const destPath = args[2] || './' + args[1] + '.downloaded';
64
+ const downloadResult = await service.downloadFile(args[1], destPath);
65
+ console.log('✅ 文件下载成功!');
66
+ console.log(`📎 文件名: ${downloadResult.fileName}`);
67
+ console.log(`💾 保存路径: ${downloadResult.destinationPath}`);
68
+ break;
69
+
70
+ case 'delete':
71
+ if (!args[1]) {
72
+ console.error('❌ 请指定要删除的文件ID');
73
+ break;
74
+ }
75
+ await service.initialize();
76
+ await service.deleteFile(args[1]);
77
+ console.log('✅ 文件删除成功!');
78
+ break;
79
+
80
+ case 'mkdir':
81
+ if (!args[1]) {
82
+ console.error('❌ 请指定文件夹名称');
83
+ break;
84
+ }
85
+ await service.initialize();
86
+ const folderResult = await service.createFolder(args[1]);
87
+ console.log('✅ 文件夹创建成功!');
88
+ console.log(`📁 文件夹名: ${folderResult.name}`);
89
+ console.log(`🆔 文件夹ID: ${folderResult.id}`);
90
+ console.log(`🔗 链接: ${folderResult.webViewLink}`);
91
+ break;
92
+
93
+ case 'search':
94
+ if (!args[1]) {
95
+ console.error('❌ 请指定搜索条件');
96
+ break;
97
+ }
98
+ await service.initialize();
99
+ const searchResult = await service.listFiles({ query: args[1] });
100
+ console.log(`🔍 搜索结果 (${searchResult.files.length} 个):`);
101
+ console.log('');
102
+ searchResult.files.forEach((file, index) => {
103
+ console.log(`${index + 1}. ${formatFileInfo(file)}`);
104
+ });
105
+ break;
106
+
107
+ case 'info':
108
+ if (!args[1]) {
109
+ console.error('❌ 请指定文件ID');
110
+ break;
111
+ }
112
+ await service.initialize();
113
+ const info = await service.getFileInfo(args[1]);
114
+ console.log('📋 文件信息:');
115
+ console.log(`📎 名称: ${info.name}`);
116
+ console.log(`🆔 ID: ${info.id}`);
117
+ console.log(`📊 类型: ${info.mimeType}`);
118
+ console.log(`💾 大小: ${info.size || 'N/A'}`);
119
+ console.log(`📅 创建时间: ${info.createdTime}`);
120
+ console.log(`🔄 修改时间: ${info.modifiedTime}`);
121
+ break;
122
+
123
+ case 'test':
124
+ await service.initialize();
125
+ const testInfo = await service.testConnection();
126
+ console.log('✅ 连接成功!');
127
+ console.log(`👤 用户: ${testInfo.user.emailAddress}`);
128
+ console.log(`📊 存储配额: ${testInfo.storageQuota.usage} / ${testInfo.storageQuota.limit}`);
129
+ break;
130
+
131
+ default:
132
+ console.error(`❌ 未知命令: ${command}`);
133
+ showUsage();
134
+ }
135
+ } catch (error) {
136
+ console.error('❌ 操作失败:', error.message);
137
+ process.exit(1);
138
+ }
139
+ }
140
+
141
+ if (require.main === module) { main(); }
142
+
143
+ module.exports = { main };
service/gdrive/gdrive-cli.js ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Google Drive 命令行接口
5
+ * 统一的命令行工具,支持所有基本操作
6
+ */
7
+
8
+ const GoogleDriveOAuth2Service = require('./gdrive-service');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+
12
+ /**
13
+ * 显示使用帮助
14
+ */
15
+ function showUsage() {
16
+ console.log('');
17
+ console.log('📁 Google Drive 命令行工具');
18
+ console.log('');
19
+ console.log('🔧 认证命令:');
20
+ console.log(' node gdrive-cli.js auth - 进行OAuth2认证');
21
+ console.log(' node gdrive-cli.js refresh - 刷新访问令牌');
22
+ console.log(' node gdrive-cli.js test - 测试连接');
23
+ console.log('');
24
+ console.log('📄 文件操作:');
25
+ console.log(' node gdrive-cli.js list [文件夹ID] [数量] - 列出文件');
26
+ console.log(' node gdrive-cli.js upload <文件路径> [文件夹ID] - 上传文件');
27
+ console.log(' node gdrive-cli.js download <文件ID> [保存路径] - 下载文件');
28
+ console.log(' node gdrive-cli.js delete <文件ID> - 删除文件');
29
+ console.log(' node gdrive-cli.js mkdir <文件夹名> [父文件夹ID] - 创建文件夹');
30
+ console.log(' node gdrive-cli.js search <搜索条件> - 搜索文件');
31
+ console.log(' node gdrive-cli.js info <文件ID> - 获取文件信息');
32
+ console.log('');
33
+ console.log('📋 示例:');
34
+ console.log(' node gdrive-cli.js list # 列出根目录文件');
35
+ console.log(' node gdrive-cli.js upload ./test.txt # 上传文件');
36
+ console.log(' node gdrive-cli.js download 1Bxi... ./save.txt # 下载文件');
37
+ console.log(' node gdrive-cli.js search "name contains \\".txt\\"" # 搜索txt文件');
38
+ console.log('');
39
+ }
40
+
41
+ /**
42
+ * 格式化文件信息显示
43
+ */
44
+ function formatFileInfo(file) {
45
+ const size = file.size ? `${(parseInt(file.size) / 1024).toFixed(2)} KB` : 'N/A';
46
+ const type = file.mimeType.includes('folder') ? '📁 文件夹' :
47
+ file.mimeType.includes('google-apps') ? '📄 Google文档' : '📎 文件';
48
+
49
+ return `${type} ${file.name} (${size})`;
50
+ }
51
+
52
+ /**
53
+ * 格式化文件列表显示
54
+ */
55
+ function formatFileList(files) {
56
+ if (!files || files.length === 0) {
57
+ console.log('📭 没有找到文件');
58
+ return;
59
+ }
60
+
61
+ console.log(`📋 找到 ${files.length} 个文件:`);
62
+ console.log('');
63
+
64
+ files.forEach((file, index) => {
65
+ const size = file.size ? `${(parseInt(file.size) / 1024).toFixed(2)} KB` : 'N/A';
66
+ const type = file.mimeType.includes('folder') ? '📁' :
67
+ file.mimeType.includes('google-apps') ? '📄' : '📎';
68
+
69
+ console.log(`${index + 1}. ${type} ${file.name}`);
70
+ console.log(` ID: ${file.id}`);
71
+ console.log(` 大小: ${size}`);
72
+ console.log(` 修改时间: ${file.modifiedTime || file.createdTime}`);
73
+ console.log('');
74
+ });
75
+ }
76
+
77
+ /**
78
+ * 主函数
79
+ */
80
+ async function main() {
81
+ const args = process.argv.slice(2);
82
+
83
+ if (args.length === 0) {
84
+ showUsage();
85
+ return;
86
+ }
87
+
88
+ const command = args[0];
89
+
90
+ // 读取配置文件
91
+ let defaultFolderId = null;
92
+ const configPath = path.join(__dirname, 'config.json');
93
+ if (fs.existsSync(configPath)) {
94
+ try {
95
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
96
+ defaultFolderId = config.defaultFolder?.id || null;
97
+ } catch (error) {
98
+ console.warn('配置文件读取失败,使用默认设置');
99
+ }
100
+ }
101
+
102
+ const service = new GoogleDriveOAuth2Service(defaultFolderId);
103
+
104
+ try {
105
+ switch (command) {
106
+ case 'auth':
107
+ await GoogleDriveOAuth2Service.getNewToken();
108
+ break;
109
+
110
+ case 'refresh':
111
+ await GoogleDriveOAuth2Service.refreshToken();
112
+ break;
113
+
114
+ case 'test':
115
+ await service.initialize();
116
+ const info = await service.testConnection();
117
+ console.log('✅ 连接成功!');
118
+ console.log(`👤 用户: ${info.user.emailAddress}`);
119
+ console.log(`📊 存储空间: ${info.storageQuota.usage || 'N/A'} / ${info.storageQuota.limit || 'N/A'}`);
120
+ break;
121
+
122
+ case 'list':
123
+ await service.initialize();
124
+ const folderId = args[1] || null;
125
+ const pageSize = args[2] ? parseInt(args[2]) : 10;
126
+ const listResult = await service.listFiles({ folderId, pageSize });
127
+ formatFileList(listResult.files);
128
+ break;
129
+
130
+ case 'upload':
131
+ if (args.length < 2) {
132
+ console.error('❌ 请指定要上传的文件路径');
133
+ return;
134
+ }
135
+ await service.initialize();
136
+ const uploadResult = await service.uploadFile(args[1], null, args[2]);
137
+ console.log('✅ 上传成功!');
138
+ console.log(`📄 文件名: ${uploadResult.name}`);
139
+ console.log(`🆔 文件ID: ${uploadResult.id}`);
140
+ console.log(`🔗 查看链接: ${uploadResult.webViewLink}`);
141
+ break;
142
+
143
+ case 'download':
144
+ if (args.length < 2) {
145
+ console.error('❌ 请指定要下载的文件ID');
146
+ return;
147
+ }
148
+ await service.initialize();
149
+ const savePath = args[2] || path.join(process.cwd(), 'downloaded_file');
150
+ const downloadResult = await service.downloadFile(args[1], savePath);
151
+ console.log('✅ 下载成功!');
152
+ console.log(`📄 文件名: ${downloadResult.fileName}`);
153
+ console.log(`💾 保存路径: ${downloadResult.destinationPath}`);
154
+ console.log(`📏 文件大小: ${downloadResult.fileSize} 字节`);
155
+ break;
156
+
157
+ case 'delete':
158
+ if (args.length < 2) {
159
+ console.error('❌ 请指定要删除的文件ID');
160
+ return;
161
+ }
162
+ await service.initialize();
163
+ await service.deleteFile(args[1]);
164
+ console.log('✅ 文件删除成功!');
165
+ break;
166
+
167
+ case 'mkdir':
168
+ if (args.length < 2) {
169
+ console.error('❌ 请指定文件夹名称');
170
+ return;
171
+ }
172
+ await service.initialize();
173
+ const folderResult = await service.createFolder(args[1], args[2]);
174
+ console.log('✅ 文件夹创建成功!');
175
+ console.log(`📁 文件夹名: ${folderResult.name}`);
176
+ console.log(`🆔 文件夹ID: ${folderResult.id}`);
177
+ console.log(`🔗 查看链接: ${folderResult.webViewLink}`);
178
+ break;
179
+
180
+ case 'search':
181
+ if (args.length < 2) {
182
+ console.error('❌ 请指定搜索条件');
183
+ return;
184
+ }
185
+ await service.initialize();
186
+ const searchResult = await service.searchFiles(args[1]);
187
+ formatFileList(searchResult.files);
188
+ break;
189
+
190
+ case 'info':
191
+ if (args.length < 2) {
192
+ console.error('❌ 请指定文件ID');
193
+ return;
194
+ }
195
+ await service.initialize();
196
+ const fileInfo = await service.getFileInfo(args[1]);
197
+ console.log('📄 文件信息:');
198
+ console.log(`📝 名称: ${fileInfo.name}`);
199
+ console.log(`🆔 ID: ${fileInfo.id}`);
200
+ console.log(`📋 类型: ${fileInfo.mimeType}`);
201
+ console.log(`📏 大小: ${fileInfo.size || 'N/A'} 字节`);
202
+ console.log(`📅 创建时间: ${fileInfo.createdTime}`);
203
+ console.log(`🔄 修改时间: ${fileInfo.modifiedTime}`);
204
+ if (fileInfo.parents && fileInfo.parents.length > 0) {
205
+ console.log(`📁 父文件夹: ${fileInfo.parents.join(', ')}`);
206
+ }
207
+ break;
208
+
209
+ case 'help':
210
+ case '--help':
211
+ case '-h':
212
+ showUsage();
213
+ break;
214
+
215
+ default:
216
+ console.error(`❌ 未知命令: ${command}`);
217
+ showUsage();
218
+ }
219
+ } catch (error) {
220
+ console.error('❌ 操作失败:', error.message);
221
+
222
+ // 提供解决建议
223
+ if (error.message.includes('credentials') || error.message.includes('认证')) {
224
+ console.log('💡 建议: 请先运行 "node gdrive-cli.js auth" 进行认证');
225
+ } else if (error.message.includes('file not found') || error.message.includes('文件不存在')) {
226
+ console.log('💡 建议: 请检查文件路径或文件ID是否正确');
227
+ } else if (error.message.includes('permission') || error.message.includes('权限')) {
228
+ console.log('💡 建议: 请检查Google Drive API权限设置');
229
+ }
230
+
231
+ process.exit(1);
232
+ }
233
+ }
234
+
235
+ // 如果直接运行此脚本
236
+ if (require.main === module) {
237
+ main();
238
+ }
239
+
240
+ module.exports = { main, showUsage };
service/gdrive/gdrive-service.js ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Google Drive OAuth2.0 桌面客户端统一服务
5
+ * 支持列举、下载、上传、删除等基本操作
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { google } = require('googleapis');
11
+ const readline = require('readline');
12
+
13
+ // 配置信息
14
+ const SCOPES = ['https://www.googleapis.com/auth/drive'];
15
+ const TOKEN_PATH = path.join(__dirname, 'token.json');
16
+ const CREDENTIALS_PATH = path.join(__dirname, 'credentials.json');
17
+
18
+ class GoogleDriveOAuth2Service {
19
+ constructor(defaultFolderId = null) {
20
+ this.auth = null;
21
+ this.drive = null;
22
+ this.defaultFolderId = defaultFolderId;
23
+ }
24
+
25
+ /**
26
+ * 初始化认证
27
+ */
28
+ async initialize() {
29
+ try {
30
+ if (!fs.existsSync(CREDENTIALS_PATH)) {
31
+ throw new Error('凭据文件不存在: ' + CREDENTIALS_PATH);
32
+ }
33
+
34
+ const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf8'));
35
+ const { client_secret, client_id, redirect_uris } = credentials.web || credentials.installed;
36
+
37
+ this.auth = new google.auth.OAuth2(
38
+ client_id,
39
+ client_secret,
40
+ redirect_uris ? redirect_uris[0] : 'http://localhost:8080'
41
+ );
42
+
43
+ // 检查是否已有token
44
+ if (fs.existsSync(TOKEN_PATH)) {
45
+ const token = JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));
46
+ this.auth.setCredentials(token);
47
+ } else {
48
+ throw new Error('需要先进行认证,请运行: node gdrive-cli.js auth');
49
+ }
50
+
51
+ this.drive = google.drive({ version: 'v3', auth: this.auth });
52
+ return this;
53
+ } catch (error) {
54
+ console.error('初始化失败:', error.message);
55
+ throw error;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * 获取新的访问令牌
61
+ */
62
+ static async getNewToken() {
63
+ try {
64
+ if (!fs.existsSync(CREDENTIALS_PATH)) {
65
+ console.error('凭据文件不存在:', CREDENTIALS_PATH);
66
+ console.log('请从 Google Cloud Console 下载凭据文件并保存为 credentials.json');
67
+ process.exit(1);
68
+ }
69
+
70
+ const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf8'));
71
+ const { client_secret, client_id, redirect_uris } = credentials.web || credentials.installed;
72
+ const oAuth2Client = new google.auth.OAuth2(
73
+ client_id,
74
+ client_secret,
75
+ redirect_uris ? redirect_uris[0] : 'http://localhost:8080'
76
+ );
77
+
78
+ const authUrl = oAuth2Client.generateAuthUrl({
79
+ access_type: 'offline',
80
+ scope: SCOPES,
81
+ });
82
+
83
+ console.log('请在浏览器中打开以下链接进行认证:');
84
+ console.log(authUrl);
85
+ console.log('');
86
+
87
+ const rl = readline.createInterface({
88
+ input: process.stdin,
89
+ output: process.stdout
90
+ });
91
+
92
+ const code = await new Promise((resolve) => {
93
+ rl.question('请输入认证代码: ', (code) => {
94
+ rl.close();
95
+ resolve(code);
96
+ });
97
+ });
98
+
99
+ const token = await oAuth2Client.getAccessToken(code);
100
+
101
+ // 保存令牌
102
+ fs.writeFileSync(TOKEN_PATH, JSON.stringify({
103
+ access_token: token.token,
104
+ refresh_token: oAuth2Client.credentials.refresh_token,
105
+ scope: SCOPES.join(' '),
106
+ token_type: 'Bearer',
107
+ expiry_date: oAuth2Client.credentials.expiry_date
108
+ }));
109
+
110
+ console.log('认证成功! 令牌已保存到 token.json');
111
+ console.log('现在可以使用 gdrive-cli.js 脚本了');
112
+
113
+ } catch (error) {
114
+ console.error('认证失败:', error.message);
115
+ process.exit(1);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * 刷新访问令牌
121
+ */
122
+ static async refreshToken() {
123
+ try {
124
+ if (!fs.existsSync(TOKEN_PATH)) {
125
+ console.error('令牌文件不存在,请先进行认证');
126
+ return;
127
+ }
128
+
129
+ const token = JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));
130
+ const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf8'));
131
+
132
+ const { client_secret, client_id } = credentials.web || credentials.installed;
133
+ const oAuth2Client = new google.auth.OAuth2(
134
+ client_id,
135
+ client_secret
136
+ );
137
+
138
+ oAuth2Client.setCredentials(token);
139
+
140
+ // 刷新令牌
141
+ ({ credentials } = await oAuth2Client.refreshAccessToken());
142
+
143
+ // 更新令牌文件
144
+ fs.writeFileSync(TOKEN_PATH, JSON.stringify({
145
+ access_token: credentials.access_token,
146
+ refresh_token: credentials.refresh_token || token.refresh_token,
147
+ scope: credentials.scope,
148
+ token_type: credentials.token_type,
149
+ expiry_date: credentials.expiry_date
150
+ }));
151
+
152
+ console.log('令牌刷新成功!');
153
+ } catch (error) {
154
+ console.error('刷新令牌失败:', error.message);
155
+ console.log('请重新进行认证');
156
+ }
157
+ }
158
+
159
+ /**
160
+ * 列出文件和文件夹
161
+ * @param {Object} options - 选项参数
162
+ * @param {string} options.folderId - 文件夹ID (可选)
163
+ * @param {number} options.pageSize - 每页文件数量 (默认10)
164
+ * @param {string} options.query - 搜索查询条件 (可选)
165
+ */
166
+ async listFiles(options = {}) {
167
+ try {
168
+ let { folderId = null, pageSize = 10, query = null } = options;
169
+
170
+ // 使用默认文件夹或指定的文件夹
171
+ folderId = folderId || this.defaultFolderId;
172
+
173
+ let searchQuery = "trashed=false";
174
+ if (folderId) {
175
+ searchQuery += ` and '${folderId}' in parents`;
176
+ }
177
+ if (query) {
178
+ searchQuery += ` and ${query}`;
179
+ }
180
+
181
+ const response = await this.drive.files.list({
182
+ q: searchQuery,
183
+ pageSize: pageSize,
184
+ fields: 'nextPageToken, files(id, name, size, mimeType, createdTime, modifiedTime)',
185
+ });
186
+
187
+ return response.data;
188
+ } catch (error) {
189
+ console.error('列出文件失败:', error.message);
190
+ throw error;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * 上传文件
196
+ * @param {string} filePath - 本地文件路径
197
+ * @param {string} fileName - 云端文件名 (可选,默认使用本地文件名)
198
+ * @param {string} folderId - 目标文件夹ID (可选)
199
+ */
200
+ async uploadFile(filePath, fileName = null, folderId = null) {
201
+ try {
202
+ if (!fs.existsSync(filePath)) {
203
+ throw new Error(`文件不存在: ${filePath}`);
204
+ }
205
+
206
+ const finalFileName = fileName || path.basename(filePath);
207
+ const fileMetadata = {
208
+ name: finalFileName,
209
+ };
210
+
211
+ // 使用默认文件夹或指定的文件夹
212
+ const targetFolderId = folderId || this.defaultFolderId;
213
+ if (targetFolderId) {
214
+ fileMetadata.parents = [targetFolderId];
215
+ }
216
+
217
+ const media = {
218
+ mimeType: 'application/octet-stream',
219
+ body: fs.createReadStream(filePath),
220
+ };
221
+
222
+ const response = await this.drive.files.create({
223
+ resource: fileMetadata,
224
+ media: media,
225
+ fields: 'id,name,size,mimeType,webViewLink',
226
+ });
227
+
228
+ return response.data;
229
+ } catch (error) {
230
+ console.error('上传文件失败:', error.message);
231
+ throw error;
232
+ }
233
+ }
234
+
235
+ /**
236
+ * 下载文件
237
+ * @param {string} fileId - 文件ID
238
+ * @param {string} destinationPath - 本地保存路径
239
+ */
240
+ async downloadFile(fileId, destinationPath) {
241
+ try {
242
+ // 获取文件信息
243
+ const fileMeta = await this.drive.files.get({
244
+ fileId: fileId,
245
+ fields: 'name,size',
246
+ });
247
+
248
+ // 确保目标目录存在
249
+ const targetDir = path.dirname(destinationPath);
250
+ if (!fs.existsSync(targetDir)) {
251
+ fs.mkdirSync(targetDir, { recursive: true });
252
+ }
253
+
254
+ // 下载文件
255
+ const response = await this.drive.files.get({
256
+ fileId: fileId,
257
+ alt: 'media',
258
+ }, { responseType: 'stream' });
259
+
260
+ const dest = fs.createWriteStream(destinationPath);
261
+ response.data.pipe(dest);
262
+
263
+ return new Promise((resolve, reject) => {
264
+ dest.on('finish', () => {
265
+ resolve({
266
+ success: true,
267
+ fileName: fileMeta.data.name,
268
+ fileSize: fileMeta.data.size,
269
+ destinationPath: destinationPath
270
+ });
271
+ });
272
+ dest.on('error', reject);
273
+ });
274
+ } catch (error) {
275
+ console.error('下载文件失败:', error.message);
276
+ throw error;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * 创建文件夹
282
+ * @param {string} folderName - 文件夹名称
283
+ * @param {string} parentFolderId - 父文件夹ID (可选)
284
+ */
285
+ async createFolder(folderName, parentFolderId = null) {
286
+ try {
287
+ const fileMetadata = {
288
+ name: folderName,
289
+ mimeType: 'application/vnd.google-apps.folder',
290
+ };
291
+
292
+ // 使用默认文件夹或指定的文件夹
293
+ const targetParentId = parentFolderId || this.defaultFolderId;
294
+ if (targetParentId) {
295
+ fileMetadata.parents = [targetParentId];
296
+ }
297
+
298
+ const response = await this.drive.files.create({
299
+ resource: fileMetadata,
300
+ fields: 'id,name,webViewLink',
301
+ });
302
+
303
+ return response.data;
304
+ } catch (error) {
305
+ console.error('创建文件夹失败:', error.message);
306
+ throw error;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * 删除文件或文件夹
312
+ * @param {string} fileId - 文件或文件夹ID
313
+ */
314
+ async deleteFile(fileId) {
315
+ try {
316
+ await this.drive.files.delete({
317
+ fileId: fileId,
318
+ });
319
+ return { success: true, fileId: fileId };
320
+ } catch (error) {
321
+ console.error('删除文件失败:', error.message);
322
+ throw error;
323
+ }
324
+ }
325
+
326
+ /**
327
+ * 搜索文件
328
+ * @param {string} searchQuery - 搜索条件
329
+ */
330
+ async searchFiles(searchQuery) {
331
+ try {
332
+ const response = await this.drive.files.list({
333
+ q: searchQuery,
334
+ fields: 'nextPageToken, files(id, name, mimeType, size, createdTime, modifiedTime)',
335
+ });
336
+
337
+ return response.data;
338
+ } catch (error) {
339
+ console.error('搜索文件失败:', error.message);
340
+ throw error;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * 获取文件信息
346
+ * @param {string} fileId - 文件ID
347
+ */
348
+ async getFileInfo(fileId) {
349
+ try {
350
+ const response = await this.drive.files.get({
351
+ fileId: fileId,
352
+ fields: 'id,name,mimeType,size,createdTime,modifiedTime,parents',
353
+ });
354
+
355
+ return response.data;
356
+ } catch (error) {
357
+ console.error('获取文件信息失败:', error.message);
358
+ throw error;
359
+ }
360
+ }
361
+
362
+ /**
363
+ * 测试连接
364
+ */
365
+ async testConnection() {
366
+ try {
367
+ const response = await this.drive.about.get({
368
+ fields: 'user, storageQuota'
369
+ });
370
+ return response.data;
371
+ } catch (error) {
372
+ console.error('连接测试失败:', error.message);
373
+ throw error;
374
+ }
375
+ }
376
+ }
377
+
378
+ module.exports = GoogleDriveOAuth2Service;
379
+
380
+ // 如果直接运行此脚本
381
+ if (require.main === module) {
382
+ const args = process.argv.slice(2);
383
+
384
+ if (args.length === 0) {
385
+ console.log('使用方法:');
386
+ console.log(' node gdrive-oauth.js auth - 进行OAuth2认证');
387
+ console.log(' node gdrive-oauth.js refresh - 刷新令牌');
388
+ console.log(' node gdrive-oauth.js test - 测试连接');
389
+ return;
390
+ }
391
+
392
+ const command = args[0];
393
+
394
+ switch (command) {
395
+ case 'auth':
396
+ GoogleDriveOAuth2Service.getNewToken();
397
+ break;
398
+ case 'refresh':
399
+ GoogleDriveOAuth2Service.refreshToken();
400
+ break;
401
+ case 'test':
402
+ (async () => {
403
+ try {
404
+ const service = new GoogleDriveOAuth2Service();
405
+ await service.initialize();
406
+ const info = await service.testConnection();
407
+ console.log('连接成功!');
408
+ console.log('用户信息:', info.user);
409
+ console.log('存储配额:', info.storageQuota);
410
+ } catch (error) {
411
+ console.error('测试失败:', error.message);
412
+ }
413
+ })();
414
+ break;
415
+ default:
416
+ console.error(`未知命令: ${command}`);
417
+ console.log('可用命令: auth, refresh, test');
418
+ }
419
+ }
service/gdrive/package.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "gdrive-oauth2-client",
3
+ "version": "2.0.0",
4
+ "description": "Google Drive OAuth2.0 桌面客户端,支持完整的文件管理功能",
5
+ "main": "gdrive-service.js",
6
+ "bin": {
7
+ "gdrive": "./gdrive-cli.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node gdrive-cli.js",
11
+ "auth": "node gdrive-cli.js auth",
12
+ "refresh": "node gdrive-cli.js refresh",
13
+ "test": "node gdrive-cli.js test",
14
+ "list": "node gdrive-cli.js list",
15
+ "help": "node gdrive-cli.js help"
16
+ },
17
+ "dependencies": {
18
+ "googleapis": "^128.0.0"
19
+ },
20
+ "keywords": [
21
+ "google-drive",
22
+ "oauth2",
23
+ "desktop-client",
24
+ "file-management",
25
+ "upload",
26
+ "download",
27
+ "api"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "engines": {
32
+ "node": ">=14.0.0"
33
+ }
34
+ }
service/gdrive/token.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"access_token":"ya29.a0AUMWg_IvqTCIDaHD0TMLzWiW7gAa_wYf54vy9nORhlt7HPcyiLc5cWZskA8xVuE3gQTarhZpgFdFoihm48kYOwuZV_XUSgp5HqY5ISRnywhGMLDiOKAsYHOmU-sB0Z-mlkWXpU4MaqgR17o8cFqAhaT8bDT5x3FmT7Q7xDIo4k9aIub4OUuDCvp9Zyvj8VqC0Bar67saCgYKAX4SARESFQHGX2MijXx1s6MfwNHC_LQdkREqmw0206","refresh_token":"1//05BwoYmQl_5QHCgYIARAAGAUSNwF-L9IrmMQGNo9ep1SQs6vgkIMlncy_8v795g0Die4_Hq6ui92608Gpa33AOey4tMIGrM51RFc","scope":"https://www.googleapis.com/auth/drive","token_type":"Bearer","expiry_date":1769651589942}
service/nodejs-service.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ echo "将要安装 nodejs 20"
4
+ echo ""
5
+ # 安装 Node.js 和必要依赖
6
+ apt-get update
7
+ apt-get install -y curl
8
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
9
+ apt-get install -y nodejs
10
+ apt-get clean
11
+ rm -rf /var/lib/apt/lists/*
service/opencode-service.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # # 将 /root/.config 目录及子目录下所有的 .md 文件权限修改为:644
4
+ # find /root/.config -type f -name "*.md" -exec chmod 644 {} \;
5
+
6
+ # 全局安装 OpenCode AI
7
+ npm install -g opencode-ai
8
+
9
+ # 验证安装
10
+ which opencode
11
+ opencode --version
service/start-services.sh ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ /.system/service/nodejs-service.sh
5
+ /.system/service/cron-service.sh
6
+ /.system/service/opencode-service.sh
7
+
8
+ /.system/script/restore.sh || true
9
+
10
+ echo ""
11
+ echo ""
12
+ echo "安装 gdrive 依赖..."
13
+ cd /.system/service/gdrive && npm install
14
+
15
+ # echo ""
16
+ # echo ""
17
+ # echo "node /.system/service/gdrive/gdrive-cli.js auth"
18
+ # node /.system/service/gdrive/gdrive-cli.js auth
19
+
20
+ echo ""
21
+ echo ""
22
+ echo "node /.system/service/gdrive/gdrive-cli.js list"
23
+ node /.system/service/gdrive/gdrive-cli.js list
24
+
25
+ # echo ""
26
+ # echo ""
27
+ # echo "node /.system/service/gdrive/quick-auth-setup.js"
28
+ # node /.system/service/gdrive/quick-auth-setup.js
29
+
30
+ # echo ""
31
+ # echo ""
32
+ # echo "node /.system/service/gdrive/list-files.js"
33
+ # node /.system/service/gdrive/list-files.js
34
+
35
+ # echo ""
36
+ # echo ""
37
+ # echo "node /.system/service/gdrive/get-hf-token.js"
38
+ # node /.system/service/gdrive/get-hf-token.js
39
+
40
+ # echo ""
41
+ # echo ""
42
+ # echo "node /.system/service/gdrive/gdrive-hf.js check"
43
+ # node /.system/service/gdrive/gdrive-hf.js check
44
+
45
+ # echo ""
46
+ # echo ""
47
+ # echo "/.system/script/upload-example.js"
48
+ # node /.system/script/upload-example.js
49
+
50
+ echo "🚀 启动 OpenCode AI Web Interface 服务..."
51
+
52
+ # 明确禁用服务器认证,确保公开访问
53
+ export OPENCODE_SERVER_PASSWORD=""
54
+ export OPENCODE_SERVER_USERNAME=""
55
+ export OPENCODE_AUTH_REQUIRED=false
56
+
57
+ echo "🔓 认证已禁用:OPENCODE_AUTH_REQUIRED=false"
58
+ echo "🌐 启动 OpenCode 服务..."
59
+ exec opencode serve --port 7860 --hostname 0.0.0.0 --cors "*"